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