5340af8a32ddccd7c19d35e5bacbdddedf4baee9
[netvirt.git] / vpnservice / fibmanager / fibmanager-impl / src / main / java / org / opendaylight / netvirt / fibmanager / FibUtil.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
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 prefix, List<String> nextHopList,
477                                            int label, RouteOrigin origin, WriteTransaction writeConfigTxn) {
478         if (rd == null || rd.isEmpty()) {
479             LOG.error("Prefix {} not associated with vpn", prefix);
480             return;
481         }
482
483         Preconditions.checkNotNull(nextHopList, "NextHopList can't be null");
484
485         try {
486             InstanceIdentifier<VrfEntry> vrfEntryId =
487                 InstanceIdentifier.builder(FibEntries.class)
488                     .child(VrfTables.class, new VrfTablesKey(rd))
489                     .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
490             Optional<VrfEntry> entry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
491
492             if (!entry.isPresent()) {
493                 VrfEntry vrfEntry = new VrfEntryBuilder().setDestPrefix(prefix).setNextHopAddressList(nextHopList)
494                     .setLabel((long) label).setOrigin(origin.getValue()).build();
495
496                 if (writeConfigTxn != null) {
497                     writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
498                 } else {
499                     MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
500                 }
501                 LOG.debug("Created vrfEntry for {} nexthop {} label {}", prefix, nextHopList, label);
502             } else { // Found in MDSAL database
503                 List<String> nh = entry.get().getNextHopAddressList();
504                 for (String nextHop : nextHopList) {
505                     if (!nh.contains(nextHop)) {
506                         nh.add(nextHop);
507                     }
508                 }
509                 VrfEntry vrfEntry = new VrfEntryBuilder().setDestPrefix(prefix).setNextHopAddressList(nh)
510                     .setLabel((long) label).setOrigin(origin.getValue()).build();
511
512                 if (writeConfigTxn != null) {
513                     writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
514                 } else {
515                     MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
516                 }
517                 LOG.debug("Updated vrfEntry for {} nexthop {} label {}", prefix, nh, label);
518             }
519         } catch (Exception e) {
520             LOG.error("addFibEntryToDS: error ", e);
521         }
522     }
523
524     // TODO Clean up the exception handling
525     @SuppressWarnings("checkstyle:IllegalCatch")
526     public static void addFibEntryForRouterInterface(DataBroker broker,
527                                                      String rd,
528                                                      String prefix,
529                                                      RouterInterface routerInterface,
530                                                      long label,
531                                                      WriteTransaction writeConfigTxn) {
532         if (rd == null || rd.isEmpty()) {
533             LOG.error("Prefix {} not associated with vpn", prefix);
534             return;
535         }
536
537         try {
538             InstanceIdentifier<VrfEntry> vrfEntryId =
539                 InstanceIdentifier.builder(FibEntries.class)
540                     .child(VrfTables.class, new VrfTablesKey(rd))
541                     .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
542
543             VrfEntry vrfEntry = new VrfEntryBuilder().setKey(new VrfEntryKey(prefix)).setDestPrefix(prefix)
544                 .setNextHopAddressList(Collections.singletonList(""))
545                 .setLabel(label)
546                 .setOrigin(RouteOrigin.LOCAL.getValue())
547                 .addAugmentation(RouterInterface.class, routerInterface).build();
548
549             if (writeConfigTxn != null) {
550                 writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
551             } else {
552                 MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
553             }
554             LOG.debug("Created vrfEntry for router-interface-prefix {} rd {} label {}", prefix, rd, label);
555         } catch (Exception e) {
556             LOG.error("addFibEntryToDS: error ", e);
557         }
558     }
559
560     public static void removeFibEntry(DataBroker broker, String rd, String prefix, WriteTransaction writeConfigTxn) {
561
562         if (rd == null || rd.isEmpty()) {
563             LOG.error("Prefix {} not associated with vpn", prefix);
564             return;
565         }
566         LOG.debug("Removing fib entry with destination prefix {} from vrf table for rd {}", prefix, rd);
567
568         InstanceIdentifier.InstanceIdentifierBuilder<VrfEntry> idBuilder =
569             InstanceIdentifier.builder(FibEntries.class)
570                 .child(VrfTables.class, new VrfTablesKey(rd)).child(VrfEntry.class, new VrfEntryKey(prefix));
571         InstanceIdentifier<VrfEntry> vrfEntryId = idBuilder.build();
572         if (writeConfigTxn != null) {
573             writeConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vrfEntryId);
574         } else {
575             MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
576         }
577     }
578
579     /**
580      * Removes a specific Nexthop from a VrfEntry. If Nexthop to remove is the
581      * last one in the VrfEntry, then the VrfEntry is removed too.
582      *
583      * @param broker          dataBroker service reference
584      * @param rd              Route-Distinguisher to which the VrfEntry belongs to
585      * @param prefix          Destination of the route
586      * @param nextHopToRemove Specific nexthop within the Route to be removed.
587      *                        If null or empty, then the whole VrfEntry is removed
588      */
589     public static void removeOrUpdateFibEntry(DataBroker broker, String rd, String prefix, String nextHopToRemove,
590                                               WriteTransaction writeConfigTxn) {
591
592         LOG.debug("Removing fib entry with destination prefix {} from vrf table for rd {}", prefix, rd);
593
594         // Looking for existing prefix in MDSAL database
595         InstanceIdentifier<VrfEntry> vrfEntryId =
596             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
597                 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
598         Optional<VrfEntry> entry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
599
600         if (entry.isPresent()) {
601             List<String> nhListRead = new ArrayList<>();
602             if (nextHopToRemove != null && !nextHopToRemove.isEmpty()) {
603                 nhListRead = entry.get().getNextHopAddressList();
604                 if (nhListRead.contains(nextHopToRemove)) {
605                     nhListRead.remove(nextHopToRemove);
606                 }
607             }
608
609             if (nhListRead.isEmpty()) {
610                 // Remove the whole entry
611                 if (writeConfigTxn != null) {
612                     writeConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vrfEntryId);
613                 } else {
614                     MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
615                 }
616                 LOG.info("Removed Fib Entry rd {} prefix {}", rd, prefix);
617             } else {
618                 // An update must be done, not including the current next hop
619                 VrfEntry vrfEntry =
620                     new VrfEntryBuilder(entry.get()).setDestPrefix(prefix).setNextHopAddressList(nhListRead)
621                         .setKey(new VrfEntryKey(prefix)).build();
622                 if (writeConfigTxn != null) {
623                     writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
624                 } else {
625                     MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
626                 }
627                 LOG.info("Removed Nexthop {} from Fib Entry rd {} prefix {}", nextHopToRemove, rd, prefix);
628             }
629         } else {
630             LOG.warn("Could not find VrfEntry for Route-Distinguisher={} and prefix={}", rd, prefix);
631         }
632     }
633
634     public static void updateFibEntry(DataBroker broker, String rd, String prefix, List<String> nextHopList,
635                                       WriteTransaction writeConfigTxn) {
636
637         LOG.debug("Updating fib entry for prefix {} with nextHopList {} for rd {}", prefix, nextHopList, rd);
638
639         // Looking for existing prefix in MDSAL database
640         InstanceIdentifier<VrfEntry> vrfEntryId =
641             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
642                 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
643         Optional<VrfEntry> entry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
644
645         if (entry.isPresent()) {
646             // Update the VRF entry with nextHopList
647             VrfEntry vrfEntry =
648                 new VrfEntryBuilder(entry.get()).setDestPrefix(prefix).setNextHopAddressList(nextHopList)
649                     .setKey(new VrfEntryKey(prefix)).build();
650             if (nextHopList.isEmpty()) {
651                 if (writeConfigTxn != null) {
652                     writeConfigTxn.put(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
653                 } else {
654                     MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
655                 }
656             } else {
657                 if (writeConfigTxn != null) {
658                     writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry, true);
659                 } else {
660                     MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId, vrfEntry);
661                 }
662             }
663             LOG.debug("Updated fib entry for prefix {} with nextHopList {} for rd {}", prefix, nextHopList, rd);
664         } else {
665             LOG.warn("Could not find VrfEntry for Route-Distinguisher={} and prefix={}", rd, prefix);
666         }
667     }
668
669     public static void addVrfTable(DataBroker broker, String rd, WriteTransaction writeConfigTxn) {
670         LOG.debug("Adding vrf table for rd {}", rd);
671         InstanceIdentifier.InstanceIdentifierBuilder<VrfTables> idBuilder =
672             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
673         InstanceIdentifier<VrfTables> vrfTableId = idBuilder.build();
674         VrfTablesBuilder vrfTablesBuilder = new VrfTablesBuilder().setKey(new VrfTablesKey(rd))
675             .setRouteDistinguisher(rd).setVrfEntry(new ArrayList<VrfEntry>());
676         if (writeConfigTxn != null) {
677             writeConfigTxn.put(LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTablesBuilder.build());
678         } else {
679             syncWrite(broker, LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTablesBuilder.build(),
680                 FibUtil.DEFAULT_CALLBACK);
681         }
682
683     }
684
685     public static void removeVrfTable(DataBroker broker, String rd, WriteTransaction writeConfigTxn) {
686         LOG.debug("Removing 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
691         if (writeConfigTxn != null) {
692             writeConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vrfTableId);
693         } else {
694             delete(broker, LogicalDatastoreType.CONFIGURATION, vrfTableId);
695         }
696     }
697
698     public static boolean isControllerManagedRoute(RouteOrigin routeOrigin) {
699         return routeOrigin == RouteOrigin.STATIC
700             || routeOrigin == RouteOrigin.CONNECTED
701             || routeOrigin == RouteOrigin.LOCAL
702             || routeOrigin == RouteOrigin.INTERVPN;
703     }
704
705     public static boolean isControllerManagedNonInterVpnLinkRoute(RouteOrigin routeOrigin) {
706         return routeOrigin == RouteOrigin.STATIC
707             || routeOrigin == RouteOrigin.CONNECTED
708             || routeOrigin == RouteOrigin.LOCAL;
709     }
710 }