Ethernet VPN Enhancement to Vpn Engine and FIB.
[netvirt.git] / vpnservice / fibmanager / fibmanager-impl / src / main / java / org / opendaylight / netvirt / fibmanager / FibUtil.java
1 /*
2  * Copyright © 2016, 2017 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
9 package org.opendaylight.netvirt.fibmanager;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.CheckedFuture;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.Future;
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
24 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
27 import org.opendaylight.genius.mdsalutil.MDSALUtil;
28 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
29 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.RouterInterface;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnIdToVpnInstance;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceToVpnId;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIdsKey;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.InterVpnLinkStates;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.InterVpnLinks;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkStateKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
61 import org.opendaylight.yangtools.yang.binding.DataObject;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.opendaylight.yangtools.yang.common.RpcResult;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67 public class FibUtil {
68     private static final Logger LOG = LoggerFactory.getLogger(FibUtil.class);
69
70     // TODO Clean up the exception handling
71     @SuppressWarnings("checkstyle:IllegalCatch")
72     public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
73                                                           InstanceIdentifier<T> path) {
74
75         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
76
77         Optional<T> result = Optional.absent();
78         try {
79             result = tx.read(datastoreType, path).get();
80         } catch (Exception e) {
81             throw new RuntimeException(e);
82         }
83
84         return result;
85     }
86
87     static <T extends DataObject> void asyncWrite(DataBroker broker, LogicalDatastoreType datastoreType,
88                                                   InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
89         WriteTransaction tx = broker.newWriteOnlyTransaction();
90         tx.merge(datastoreType, path, data, true);
91         Futures.addCallback(tx.submit(), callback);
92     }
93
94     static <T extends DataObject> void syncWrite(DataBroker broker, LogicalDatastoreType datastoreType,
95                                                  InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
96         WriteTransaction tx = broker.newWriteOnlyTransaction();
97         tx.put(datastoreType, path, data, true);
98         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
99         try {
100             futures.get();
101         } catch (InterruptedException | ExecutionException e) {
102             LOG.error("Error writing to datastore (path, data) : ({}, {})", path, data, e);
103             throw new RuntimeException(e.getMessage());
104         }
105     }
106
107     static <T extends DataObject> void delete(DataBroker broker, LogicalDatastoreType datastoreType,
108                                               InstanceIdentifier<T> path) {
109         WriteTransaction tx = broker.newWriteOnlyTransaction();
110         tx.delete(datastoreType, path);
111         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
112     }
113
114     static InstanceIdentifier<Adjacency> getAdjacencyIdentifier(String vpnInterfaceName, String ipAddress) {
115         return InstanceIdentifier.builder(org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang
116             .l3vpn.rev140815.VpnInterfaces.class)
117             .child(org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces
118                 .VpnInterface.class,
119                 new org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces
120                     .VpnInterfaceKey(vpnInterfaceName))
121             .augmentation(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies.class)
122             .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency.class,
123                 new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list
124                     .AdjacencyKey(ipAddress)).build();
125     }
126
127     static InstanceIdentifier<Adjacencies> getAdjListPath(String vpnInterfaceName) {
128         return InstanceIdentifier.builder(org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn
129             .rev140815.VpnInterfaces.class)
130             .child(org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces
131                 .VpnInterface.class,
132                 new org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces
133                     .VpnInterfaceKey(vpnInterfaceName))
134             .augmentation(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies.class)
135             .build();
136     }
137
138     static InstanceIdentifier<Prefixes> getPrefixToInterfaceIdentifier(long vpnId, String ipPrefix) {
139         return InstanceIdentifier.builder(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn
140             .rev130911.PrefixToInterface.class)
141             .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface
142                 .VpnIds.class,
143                 new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface
144                     .VpnIdsKey(vpnId))
145             .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface
146                     .vpn.ids.Prefixes.class,
147                 new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to
148                     ._interface.vpn.ids.PrefixesKey(ipPrefix)).build();
149     }
150
151     static InstanceIdentifier<VpnInterface> getVpnInterfaceIdentifier(String vpnInterfaceName) {
152         return InstanceIdentifier.builder(org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn
153             .rev140815.VpnInterfaces.class)
154             .child(org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces
155                 .VpnInterface.class,
156                 new org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces
157                     .VpnInterfaceKey(vpnInterfaceName)).build();
158     }
159
160     public static InstanceIdentifier<VpnToDpnList> getVpnToDpnListIdentifier(String rd, BigInteger dpnId) {
161         return InstanceIdentifier.builder(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn
162             .rev130911.VpnInstanceOpData.class)
163             .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
164                 .VpnInstanceOpDataEntry.class,
165                 new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
166                     .VpnInstanceOpDataEntryKey(rd))
167             .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
168                 .vpn.instance.op.data.entry.VpnToDpnList.class,
169                 new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
170                     .vpn.instance.op.data.entry.VpnToDpnListKey(dpnId)).build();
171     }
172
173     static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to
174         .extraroute.vpn.Extraroute> getVpnToExtrarouteIdentifier(String vrfId, String ipPrefix) {
175         return InstanceIdentifier.builder(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn
176             .rev130911.VpnToExtraroute.class)
177             .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroute.Vpn
178                 .class, new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to
179                 .extraroute.VpnKey(vrfId)).child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn
180                     .rev130911.vpn.to.extraroute.vpn.Extraroute.class,
181                 new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroute.vpn
182                     .ExtrarouteKey(ipPrefix)).build();
183     }
184
185     static InstanceIdentifier<VpnInstanceOpDataEntry> getVpnInstanceOpDataIdentifier(String rd) {
186         return InstanceIdentifier.builder(VpnInstanceOpData.class)
187             .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd)).build();
188     }
189
190     static Optional<VpnInstanceOpDataEntry> getVpnInstanceOpData(DataBroker broker, String rd) {
191         InstanceIdentifier<VpnInstanceOpDataEntry> id = getVpnInstanceOpDataIdentifier(rd);
192         return read(broker, LogicalDatastoreType.OPERATIONAL, id);
193     }
194
195     static String getNextHopLabelKey(String rd, String prefix) {
196         String key = rd + FibConstants.SEPARATOR + prefix;
197         return key;
198     }
199
200     static Prefixes getPrefixToInterface(DataBroker broker, Long vpnId, String ipPrefix) {
201         Optional<Prefixes> localNextHopInfoData = read(broker, LogicalDatastoreType.OPERATIONAL,
202             getPrefixToInterfaceIdentifier(vpnId, ipPrefix));
203         return localNextHopInfoData.isPresent() ? localNextHopInfoData.get() : null;
204     }
205
206     static String getMacAddressFromPrefix(DataBroker broker, String ifName, String ipPrefix) {
207         Optional<Adjacency> adjacencyData = read(broker, LogicalDatastoreType.OPERATIONAL,
208             getAdjacencyIdentifier(ifName, ipPrefix));
209         return adjacencyData.isPresent() ? adjacencyData.get().getMacAddress() : null;
210     }
211
212     static void releaseId(IdManagerService idManager, String poolName, String idKey) {
213         ReleaseIdInput idInput = new ReleaseIdInputBuilder().setPoolName(poolName).setIdKey(idKey).build();
214         try {
215             Future<RpcResult<Void>> result = idManager.releaseId(idInput);
216             RpcResult<Void> rpcResult = result.get();
217             if (!rpcResult.isSuccessful()) {
218                 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
219             }
220         } catch (InterruptedException | ExecutionException e) {
221             LOG.warn("Exception when getting Unique Id for key {}", idKey, e);
222         }
223     }
224
225     static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn
226         .instance.to.vpn.id.VpnInstance> getVpnInstanceToVpnIdIdentifier(String vpnName) {
227         return InstanceIdentifier.builder(VpnInstanceToVpnId.class)
228             .child(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
229                     .VpnInstance.class,
230                 new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
231                     .VpnInstanceKey(vpnName)).build();
232     }
233
234     public static long getVpnId(DataBroker broker, String vpnName) {
235
236         InstanceIdentifier<VpnInstance> id = getVpnInstanceToVpnIdIdentifier(vpnName);
237         return read(broker, LogicalDatastoreType.CONFIGURATION, id).transform(VpnInstance::getVpnId).or(-1L);
238     }
239
240     /**
241      * Retrieves the VpnInstance name (typically the VPN Uuid) out from the route-distinguisher.
242      *
243      * @param broker The DataBroker
244      * @param rd The route-distinguisher
245      * @return The vpn instance
246      */
247     public static Optional<String> getVpnNameFromRd(DataBroker broker, String rd) {
248         return getVpnInstanceOpData(broker, rd).transform(VpnInstanceOpDataEntry::getVpnInstanceName);
249     }
250
251     static List<InterVpnLink> getAllInterVpnLinks(DataBroker broker) {
252         InstanceIdentifier<InterVpnLinks> interVpnLinksIid = InstanceIdentifier.builder(InterVpnLinks.class).build();
253
254         return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, interVpnLinksIid).transform(
255             InterVpnLinks::getInterVpnLink).or(new ArrayList<>());
256     }
257
258     /**
259      * Returns the instance identifier for a given vpnLinkName.
260      *
261      * @param vpnLinkName The vpn link name
262      * @return InstanceIdentifier
263      */
264     public static InstanceIdentifier<InterVpnLinkState> getInterVpnLinkStateIid(String vpnLinkName) {
265         return InstanceIdentifier.builder(InterVpnLinkStates.class)
266             .child(InterVpnLinkState.class, new InterVpnLinkStateKey(vpnLinkName)).build();
267     }
268
269     /**
270      * Checks if the InterVpnLink is in Active state.
271      *
272      * @param broker The DataBroker
273      * @param vpnLinkName The vpn linkname
274      * @return The link state
275      */
276     public static boolean isInterVpnLinkActive(DataBroker broker, String vpnLinkName) {
277         Optional<InterVpnLinkState> interVpnLinkState = getInterVpnLinkState(broker, vpnLinkName);
278         if (!interVpnLinkState.isPresent()) {
279             LOG.warn("Could not find Operative State for InterVpnLink {}", vpnLinkName);
280             return false;
281         }
282
283         return interVpnLinkState.get().getState().equals(InterVpnLinkState.State.Active);
284     }
285
286     /**
287      * Checks if the state of the interVpnLink.
288      *
289      * @param broker The DataBroker
290      * @param vpnLinkName The vpn linkname
291      * @return The link state
292      */
293     public static Optional<InterVpnLinkState> getInterVpnLinkState(DataBroker broker, String vpnLinkName) {
294         InstanceIdentifier<InterVpnLinkState> vpnLinkStateIid = getInterVpnLinkStateIid(vpnLinkName);
295         return read(broker, LogicalDatastoreType.CONFIGURATION, vpnLinkStateIid);
296     }
297
298     /**
299      * Retrieves the InterVpnLink in which the VPN, represented by its Uuid,
300      * participates.
301      *
302      * @param dataBroker The DataBroker
303      * @param vpnUuid The vpn uuid
304      * @return The InterVpnLink or Optional.absent() if the VPN does not participate in an InterVpnLink
305      */
306     public static Optional<InterVpnLink> getInterVpnLinkByVpnUuid(DataBroker dataBroker, String vpnUuid) {
307         List<InterVpnLink> interVpnLinkList = getAllInterVpnLinks(dataBroker);
308         for (InterVpnLink interVpnLink : interVpnLinkList) {
309             if (interVpnLink.getFirstEndpoint().getVpnUuid().getValue().equals(vpnUuid)
310                 || interVpnLink.getSecondEndpoint().getVpnUuid().getValue().equals(vpnUuid)) {
311                 LOG.debug("InterVpnLink found for VPN {}. Details: vpn1=( uuid={} endpoint={}) "
312                     + "vpn2=( uuid={} endpoint={} ))",
313                     vpnUuid, interVpnLink.getFirstEndpoint().getVpnUuid(),
314                     interVpnLink.getFirstEndpoint().getIpAddress(), interVpnLink.getSecondEndpoint().getVpnUuid(),
315                     interVpnLink.getSecondEndpoint().getIpAddress());
316                 return Optional.fromNullable(interVpnLink);
317             }
318         }
319         LOG.debug("Could not find a suitable InterVpnLink for VpnUuid={}", vpnUuid);
320         return Optional.absent();
321     }
322
323     /**
324      * Retrieves the InterVpnLink in which the VPN, represented by its
325      * Route-Distinguisher, participates.
326      *
327      * @param dataBroker The DataBroker
328      * @param rd route-distinguisher
329      * @return The InterVpnLink or Optional.absent() if the VPN does not participate in an InterVpnLink
330      */
331     public static Optional<InterVpnLink> getInterVpnLinkByRd(DataBroker dataBroker, String rd) {
332         Optional<String> vpnId = getVpnNameFromRd(dataBroker, rd);
333         if (!vpnId.isPresent()) {
334             LOG.debug("Could not find vpnId for RouteDistinguisher {}", rd);
335             return Optional.absent();
336         }
337
338         return getInterVpnLinkByVpnUuid(dataBroker, vpnId.get());
339     }
340
341     /**
342      * Checks if the route-distinguisher is involved in any inter-vpn-link, which is returned if its found.
343      *
344      * @param dataBroker The DataBroker
345      * @param rd route-distinguisher
346      * @return The inter vpn link
347      */
348     public static Optional<InterVpnLink> getActiveInterVpnLinkFromRd(DataBroker dataBroker, String rd) {
349         Optional<InterVpnLink> interVpnLink = getInterVpnLinkByRd(dataBroker, rd);
350         if (interVpnLink.isPresent()) {
351             if (isInterVpnLinkActive(dataBroker, interVpnLink.get().getName())) {
352                 return interVpnLink;
353             } else {
354                 LOG.warn("InterVpnLink for RouteDistinguisher {} exists, but it's in error state. InterVpnLink={}",
355                     rd, interVpnLink.get().getName());
356                 return Optional.absent();
357             }
358         }
359         return Optional.absent();
360     }
361
362     /**
363      * Checks if the route-distinguisher is involved in any inter-vpn-link. In that case, this method will return
364      * the endpoint of the other vpn involved in the inter-vpn-link.
365      *
366      * @param dataBroker The DataBroker
367      * @param rd route-distinguisher
368      * @return Opposite endpoint
369      */
370     public static Optional<String> getInterVpnLinkOppositeEndPointIpAddress(DataBroker dataBroker, String rd) {
371         Optional<String> vpnId = getVpnNameFromRd(dataBroker, rd);
372         if (!vpnId.isPresent()) {
373             LOG.debug("Could not find the VpnName for RouteDistinguisher {}", rd);
374             return Optional.absent();
375         }
376         List<InterVpnLink> interVpnLinkList = getAllInterVpnLinks(dataBroker);
377         if (!interVpnLinkList.isEmpty()) {
378             for (InterVpnLink interVpnLink : interVpnLinkList) {
379                 if (interVpnLink.getFirstEndpoint().getVpnUuid().getValue().equals(vpnId)) {
380                     return Optional.fromNullable(interVpnLink.getSecondEndpoint().getVpnUuid().getValue());
381                 } else if (interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(vpnId)) {
382                     return Optional.fromNullable(interVpnLink.getFirstEndpoint().getIpAddress().getValue());
383                 }
384             }
385         }
386         return Optional.absent();
387     }
388
389     /**
390      * Obtains the route-distinguisher for a given vpn-name.
391      *
392      * @param broker The DataBroker
393      * @param vpnName vpn name
394      * @return route-distinguisher
395      */
396     public static String getVpnRd(DataBroker broker, String vpnName) {
397         InstanceIdentifier<VpnInstance> id = getVpnInstanceToVpnIdIdentifier(vpnName);
398         return read(broker, LogicalDatastoreType.CONFIGURATION, id).transform(VpnInstance::getVrfId).orNull();
399     }
400
401     /**
402      * Returns a boolean value which indicates if the endpoint's IP received as parameter belongs to any InterVpnLink.
403      *
404      * @param broker DataBroker
405      * @param endpointIp IP to search for.
406      * @return inter vpn link
407      */
408     public static boolean getInterVpnLinkByEndpointIp(DataBroker broker, String endpointIp) {
409         List<InterVpnLink> allInterVpnLinks = getAllInterVpnLinks(broker);
410         for (InterVpnLink interVpnLink : allInterVpnLinks) {
411             if (interVpnLink.getFirstEndpoint().getIpAddress().getValue().equals(endpointIp)
412                 || interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(endpointIp)) {
413                 return true;
414             }
415         }
416         return false;
417     }
418
419     public static int getUniqueId(IdManagerService idManager, String poolName, String idKey) {
420         AllocateIdInput getIdInput = new AllocateIdInputBuilder().setPoolName(poolName).setIdKey(idKey).build();
421
422         try {
423             Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
424             RpcResult<AllocateIdOutput> rpcResult = result.get();
425             if (rpcResult.isSuccessful()) {
426                 return rpcResult.getResult().getIdValue().intValue();
427             } else {
428                 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
429             }
430         } catch (InterruptedException | ExecutionException e) {
431             LOG.warn("Exception when getting Unique Id", e);
432         }
433         return 0;
434     }
435
436     static final FutureCallback<Void> DEFAULT_CALLBACK =
437         new FutureCallback<Void>() {
438             @Override
439             public void onSuccess(Void result) {
440                 LOG.debug("Success in Datastore operation");
441             }
442
443             @Override
444             public void onFailure(Throwable error) {
445                 LOG.error("Error in Datastore operation", error);
446             }
447
448             ;
449         };
450
451     public static String getVpnNameFromId(DataBroker broker, long vpnId) {
452         InstanceIdentifier<VpnIds> id = getVpnIdToVpnInstanceIdentifier(vpnId);
453         return read(broker, LogicalDatastoreType.CONFIGURATION, id).transform(VpnIds::getVpnInstanceName).orNull();
454     }
455
456     static InstanceIdentifier<VpnIds> getVpnIdToVpnInstanceIdentifier(long vpnId) {
457         return InstanceIdentifier.builder(VpnIdToVpnInstance.class)
458             .child(VpnIds.class, new VpnIdsKey(vpnId)).build();
459     }
460
461     public static <T extends DataObject> void syncUpdate(DataBroker broker, LogicalDatastoreType datastoreType,
462                                                          InstanceIdentifier<T> path, T data) {
463         WriteTransaction tx = broker.newWriteOnlyTransaction();
464         tx.put(datastoreType, path, data, true);
465         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
466         try {
467             futures.get();
468         } catch (InterruptedException | ExecutionException e) {
469             LOG.error("Error writing to datastore (path, data) : ({}, {})", path, data, e);
470             throw new RuntimeException(e.getMessage());
471         }
472     }
473
474     // TODO Clean up the exception handling
475     @SuppressWarnings("checkstyle:IllegalCatch")
476     public static void addOrUpdateFibEntry(DataBroker broker, String rd, String macAddress, String prefix,
477                                            List<String> nextHopList, VrfEntry.EncapType encapType, int label,
478                                            long l3vni, String gwMacAddress, RouteOrigin origin,
479                                            WriteTransaction writeConfigTxn) {
480         if (rd == null || rd.isEmpty()) {
481             LOG.error("Prefix {} not associated with vpn", prefix);
482             return;
483         }
484
485         Preconditions.checkNotNull(nextHopList, "NextHopList can't be null");
486
487         try {
488             InstanceIdentifier<VrfEntry> vrfEntryId =
489                 InstanceIdentifier.builder(FibEntries.class)
490                     .child(VrfTables.class, new VrfTablesKey(rd))
491                     .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
492             Optional<VrfEntry> entry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
493
494             if (! entry.isPresent()) {
495                 writeFibEntryToDs(vrfEntryId, prefix, nextHopList, label, l3vni, encapType, origin, macAddress,
496                         gwMacAddress, writeConfigTxn, broker);
497                 LOG.debug("Created vrfEntry for {} nexthop {} label {}", prefix, nextHopList, label);
498             } else { // Found in MDSAL database
499                 List<String> nh = entry.get().getNextHopAddressList();
500                 for (String nextHop : nextHopList) {
501                     if (!nh.contains(nextHop)) {
502                         nh.add(nextHop);
503                     }
504                 }
505                 writeFibEntryToDs(vrfEntryId, prefix, nh, label, l3vni, encapType, origin, macAddress,
506                         gwMacAddress, writeConfigTxn, broker);
507                 LOG.debug("Updated vrfEntry for {} nexthop {} label {}", prefix, nh, label);
508             }
509         } catch (Exception e) {
510             LOG.error("addFibEntryToDS: error ", e);
511         }
512     }
513
514     // TODO Clean up the exception handling
515     @SuppressWarnings("checkstyle:IllegalCatch")
516     public static void writeFibEntryToDs(InstanceIdentifier<VrfEntry> vrfEntryId, String prefix,
517                                          List<String> nextHopList, long label, Long l3vni,
518                                          VrfEntry.EncapType encapType, RouteOrigin origin, String macAddress,
519                                          String gatewayMacAddress, WriteTransaction writeConfigTxn,
520                                          DataBroker broker) {
521         VrfEntryBuilder vrfEntryBuilder = new VrfEntryBuilder().setDestPrefix(prefix).setNextHopAddressList(nextHopList)
522                 .setOrigin(origin.getValue());
523         buildVpnEncapSpecificInfo(vrfEntryBuilder, encapType, label, l3vni, macAddress, gatewayMacAddress);
524         if (writeConfigTxn != null) {
525             writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntryBuilder.build(), true);
526         } else {
527             MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntryBuilder.build());
528         }
529     }
530
531     @SuppressWarnings("checkstyle:IllegalCatch")
532     public static void addFibEntryForRouterInterface(DataBroker broker,
533                                                      String rd,
534                                                      String prefix,
535                                                      RouterInterface routerInterface,
536                                                      long label,
537                                                      WriteTransaction writeConfigTxn) {
538         if (rd == null || rd.isEmpty()) {
539             LOG.error("Prefix {} not associated with vpn", prefix);
540             return;
541         }
542
543         try {
544             InstanceIdentifier<VrfEntry> vrfEntryId =
545                 InstanceIdentifier.builder(FibEntries.class)
546                     .child(VrfTables.class, new VrfTablesKey(rd))
547                     .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
548
549             VrfEntry vrfEntry = new VrfEntryBuilder().setKey(new VrfEntryKey(prefix)).setDestPrefix(prefix)
550                 .setNextHopAddressList(Collections.singletonList(""))
551                 .setLabel(label)
552                 .setOrigin(RouteOrigin.LOCAL.getValue())
553                 .addAugmentation(RouterInterface.class, routerInterface).build();
554
555             if (writeConfigTxn != null) {
556                 writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
557             } else {
558                 MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
559             }
560             LOG.debug("Created vrfEntry for router-interface-prefix {} rd {} label {}", prefix, rd, label);
561         } catch (Exception e) {
562             LOG.error("addFibEntryToDS: error ", e);
563         }
564     }
565
566     private static void buildVpnEncapSpecificInfo(VrfEntryBuilder builder, VrfEntry.EncapType encapType, long label,
567                                                  long l3vni, String macAddress, String gatewayMac) {
568         if (encapType.equals(VrfEntry.EncapType.Mplsgre)) {
569             builder.setLabel(label);
570         } else {
571             builder.setL3vni(l3vni).setGatewayMacAddress(gatewayMac);
572         }
573         builder.setEncapType(encapType);
574     }
575
576     public static void removeFibEntry(DataBroker broker, String rd, String prefix, WriteTransaction writeConfigTxn) {
577
578         if (rd == null || rd.isEmpty()) {
579             LOG.error("Prefix {} not associated with vpn", prefix);
580             return;
581         }
582         LOG.debug("Removing fib entry with destination prefix {} from vrf table for rd {}", prefix, rd);
583
584         InstanceIdentifier.InstanceIdentifierBuilder<VrfEntry> idBuilder =
585             InstanceIdentifier.builder(FibEntries.class)
586                 .child(VrfTables.class, new VrfTablesKey(rd)).child(VrfEntry.class, new VrfEntryKey(prefix));
587         InstanceIdentifier<VrfEntry> vrfEntryId = idBuilder.build();
588         if (writeConfigTxn != null) {
589             writeConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vrfEntryId);
590         } else {
591             MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
592         }
593     }
594
595     /**
596      * Removes a specific Nexthop from a VrfEntry. If Nexthop to remove is the
597      * last one in the VrfEntry, then the VrfEntry is removed too.
598      *
599      * @param broker          dataBroker service reference
600      * @param rd              Route-Distinguisher to which the VrfEntry belongs to
601      * @param prefix          Destination of the route
602      * @param nextHopToRemove Specific nexthop within the Route to be removed.
603      *                        If null or empty, then the whole VrfEntry is removed
604      */
605     public static void removeOrUpdateFibEntry(DataBroker broker, String rd, String prefix, String nextHopToRemove,
606                                               WriteTransaction writeConfigTxn) {
607
608         LOG.debug("Removing fib entry with destination prefix {} from vrf table for rd {}", prefix, rd);
609
610         // Looking for existing prefix in MDSAL database
611         InstanceIdentifier<VrfEntry> vrfEntryId =
612             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
613                 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
614         Optional<VrfEntry> entry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
615
616         if (entry.isPresent()) {
617             List<String> nhListRead = new ArrayList<>();
618             if (nextHopToRemove != null && !nextHopToRemove.isEmpty()) {
619                 nhListRead = entry.get().getNextHopAddressList();
620                 if (nhListRead.contains(nextHopToRemove)) {
621                     nhListRead.remove(nextHopToRemove);
622                 }
623             }
624
625             if (nhListRead.isEmpty()) {
626                 // Remove the whole entry
627                 if (writeConfigTxn != null) {
628                     writeConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vrfEntryId);
629                 } else {
630                     MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
631                 }
632                 LOG.info("Removed Fib Entry rd {} prefix {}", rd, prefix);
633             } else {
634                 // An update must be done, not including the current next hop
635                 VrfEntry vrfEntry =
636                     new VrfEntryBuilder(entry.get()).setDestPrefix(prefix).setNextHopAddressList(nhListRead)
637                         .setKey(new VrfEntryKey(prefix)).build();
638                 if (writeConfigTxn != null) {
639                     writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
640                 } else {
641                     MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
642                 }
643                 LOG.info("Removed Nexthop {} from Fib Entry rd {} prefix {}", nextHopToRemove, rd, prefix);
644             }
645         } else {
646             LOG.warn("Could not find VrfEntry for Route-Distinguisher={} and prefix={}", rd, prefix);
647         }
648     }
649
650     public static void updateFibEntry(DataBroker broker, String rd, String prefix, List<String> nextHopList,
651                                       String gwMacAddress, WriteTransaction writeConfigTxn) {
652
653         LOG.debug("Updating fib entry for prefix {} with nextHopList {} for rd {}", prefix, nextHopList, rd);
654
655         // Looking for existing prefix in MDSAL database
656         InstanceIdentifier<VrfEntry> vrfEntryId =
657             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
658                 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
659         Optional<VrfEntry> entry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
660
661         if (entry.isPresent()) {
662             // Update the VRF entry with nextHopList
663             VrfEntry vrfEntry =
664                 new VrfEntryBuilder(entry.get()).setDestPrefix(prefix).setNextHopAddressList(nextHopList)
665                     .setGatewayMacAddress(gwMacAddress).setKey(new VrfEntryKey(prefix)).build();
666             if (nextHopList.isEmpty()) {
667                 if (writeConfigTxn != null) {
668                     writeConfigTxn.put(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
669                 } else {
670                     MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
671                 }
672             } else {
673                 if (writeConfigTxn != null) {
674                     writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
675                 } else {
676                     MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
677                 }
678             }
679             LOG.debug("Updated fib entry for prefix {} with nextHopList {} for rd {}", prefix, nextHopList, rd);
680         } else {
681             LOG.warn("Could not find VrfEntry for Route-Distinguisher={} and prefix={}", rd, prefix);
682         }
683     }
684
685     public static void addVrfTable(DataBroker broker, String rd, WriteTransaction writeConfigTxn) {
686         LOG.debug("Adding vrf table for rd {}", rd);
687         InstanceIdentifier.InstanceIdentifierBuilder<VrfTables> idBuilder =
688             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
689         InstanceIdentifier<VrfTables> vrfTableId = idBuilder.build();
690         VrfTablesBuilder vrfTablesBuilder = new VrfTablesBuilder().setKey(new VrfTablesKey(rd))
691             .setRouteDistinguisher(rd).setVrfEntry(new ArrayList<>());
692         if (writeConfigTxn != null) {
693             writeConfigTxn.put(LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTablesBuilder.build());
694         } else {
695             syncWrite(broker, LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTablesBuilder.build(),
696                 FibUtil.DEFAULT_CALLBACK);
697         }
698
699     }
700
701     public static void removeVrfTable(DataBroker broker, String rd, WriteTransaction writeConfigTxn) {
702         LOG.debug("Removing vrf table for rd {}", rd);
703         InstanceIdentifier.InstanceIdentifierBuilder<VrfTables> idBuilder =
704             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
705         InstanceIdentifier<VrfTables> vrfTableId = idBuilder.build();
706
707         if (writeConfigTxn != null) {
708             writeConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vrfTableId);
709         } else {
710             delete(broker, LogicalDatastoreType.CONFIGURATION, vrfTableId);
711         }
712     }
713
714     public static boolean isControllerManagedRoute(RouteOrigin routeOrigin) {
715         return routeOrigin == RouteOrigin.STATIC
716             || routeOrigin == RouteOrigin.CONNECTED
717             || routeOrigin == RouteOrigin.LOCAL
718             || routeOrigin == RouteOrigin.INTERVPN;
719     }
720
721     public static boolean isControllerManagedNonInterVpnLinkRoute(RouteOrigin routeOrigin) {
722         return routeOrigin == RouteOrigin.STATIC
723             || routeOrigin == RouteOrigin.CONNECTED
724             || routeOrigin == RouteOrigin.LOCAL;
725     }
726 }