Switch to JDT annotations for Nullable and NonNull
[netvirt.git] / fibmanager / impl / src / main / java / org / opendaylight / netvirt / fibmanager / NexthopManager.java
1 /*
2  * Copyright © 2015, 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 package org.opendaylight.netvirt.fibmanager;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
12 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
13
14 import com.google.common.base.Optional;
15 import com.google.common.base.Preconditions;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Locale;
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.concurrent.CopyOnWriteArrayList;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.Future;
27 import javax.annotation.PreDestroy;
28 import javax.inject.Inject;
29 import javax.inject.Singleton;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
34 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
35 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
36 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
37 import org.opendaylight.genius.itm.globals.ITMConstants;
38 import org.opendaylight.genius.mdsalutil.ActionInfo;
39 import org.opendaylight.genius.mdsalutil.BucketInfo;
40 import org.opendaylight.genius.mdsalutil.GroupEntity;
41 import org.opendaylight.genius.mdsalutil.MDSALUtil;
42 import org.opendaylight.genius.mdsalutil.NwConstants;
43 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
44 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
45 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
46 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
47 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
48 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
49 import org.opendaylight.genius.mdsalutil.actions.ActionRegMove;
50 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
51 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
52 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
53 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
54 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
55 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
56 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
57 import org.opendaylight.netvirt.elanmanager.api.IElanService;
58 import org.opendaylight.netvirt.fibmanager.api.L3VPNTransportTypes;
59 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.Tunnel;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameInputBuilder;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameOutput;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVlan;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
120 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacencies;
121 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesBuilder;
122 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesKey;
123 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpn;
124 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpnBuilder;
125 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpid.l3vpn.lb.nexthops.DpnLbNexthops;
126 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.l3vpn.lb.nexthops.Nexthops;
127 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
128 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
129 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
130 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
131 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionRegLoadNodesNodeTableFlowApplyActionsCase;
132 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
133 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
134 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
135 import org.opendaylight.yangtools.yang.common.RpcResult;
136 import org.slf4j.Logger;
137 import org.slf4j.LoggerFactory;
138
139 @Singleton
140 public class NexthopManager implements AutoCloseable {
141     private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
142     private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
143     private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
144     private static final long WAIT_TIME_TO_ACQUIRE_LOCK = 3000L;
145     private static final int SELECT_GROUP_WEIGHT = 1;
146
147     private final DataBroker dataBroker;
148     private final ManagedNewTransactionRunner txRunner;
149     private final IMdsalApiManager mdsalApiManager;
150     private final OdlInterfaceRpcService odlInterfaceRpcService;
151     private final ItmRpcService itmManager;
152     private final IdManagerService idManager;
153     private final IElanService elanService;
154     private final LockManagerService lockManager;
155     private final SalGroupService salGroupService;
156     private final JobCoordinator jobCoordinator;
157     private final FibUtil fibUtil;
158     private final IInterfaceManager interfaceManager;
159     private volatile L3VPNTransportTypes configuredTransportTypeL3VPN = L3VPNTransportTypes.Invalid;
160
161     /**
162      * Provides nexthop functions.
163      * Creates group ID pool
164      *
165      * @param dataBroker       - dataBroker reference
166      * @param mdsalApiManager  - mdsalApiManager reference
167      * @param idManager        - idManager reference
168      * @param odlInterfaceRpcService - odlInterfaceRpcService reference
169      * @param itmManager       - itmManager reference
170      */
171     @Inject
172     public NexthopManager(final DataBroker dataBroker,
173                           final IMdsalApiManager mdsalApiManager,
174                           final IdManagerService idManager,
175                           final OdlInterfaceRpcService odlInterfaceRpcService,
176                           final ItmRpcService itmManager,
177                           final LockManagerService lockManager,
178                           final IElanService elanService,
179                           final SalGroupService salGroupService,
180                           final JobCoordinator jobCoordinator,
181                           final FibUtil fibUtil,
182                           final IInterfaceManager interfaceManager) {
183         this.dataBroker = dataBroker;
184         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
185         this.mdsalApiManager = mdsalApiManager;
186         this.idManager = idManager;
187         this.odlInterfaceRpcService = odlInterfaceRpcService;
188         this.itmManager = itmManager;
189         this.elanService = elanService;
190         this.salGroupService = salGroupService;
191         this.jobCoordinator = jobCoordinator;
192         this.fibUtil = fibUtil;
193         this.lockManager = lockManager;
194         this.interfaceManager = interfaceManager;
195         createIdPool();
196     }
197
198     private void createIdPool() {
199         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
200             .setPoolName(NEXTHOP_ID_POOL_NAME)
201             .setLow(150000L)
202             .setHigh(175000L)
203             .build();
204         try {
205             Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
206             if (result != null && result.get().isSuccessful()) {
207                 LOG.info("Created IdPool for NextHopPointerPool");
208             }
209         } catch (InterruptedException | ExecutionException e) {
210             LOG.error("Failed to create idPool for NextHopPointerPool", e);
211         }
212     }
213
214     private String getNextHopKey(long vpnId, String ipAddress) {
215         return "nexthop." + vpnId + ipAddress;
216     }
217
218     String getRemoteSelectGroupKey(long vpnId, String ipAddress) {
219         return "remote.ecmp.nexthop." + vpnId + ipAddress;
220     }
221
222     String getLocalSelectGroupKey(long vpnId, String ipAddress) {
223         return "local.ecmp.nexthop." + vpnId + ipAddress;
224     }
225
226     public ItmRpcService getItmManager() {
227         return itmManager;
228     }
229
230     protected long createNextHopPointer(String nexthopKey) {
231         AllocateIdInput getIdInput = new AllocateIdInputBuilder()
232             .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
233             .build();
234         //TODO: Proper error handling once IdManager code is complete
235         try {
236             Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
237             RpcResult<AllocateIdOutput> rpcResult = result.get();
238             return rpcResult.getResult().getIdValue();
239         } catch (NullPointerException | InterruptedException | ExecutionException e) {
240             LOG.trace("", e);
241         }
242         return 0;
243     }
244
245     protected void removeNextHopPointer(String nexthopKey) {
246         ReleaseIdInput idInput = new ReleaseIdInputBuilder()
247             .setPoolName(NEXTHOP_ID_POOL_NAME)
248             .setIdKey(nexthopKey).build();
249         try {
250             RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
251             if (!rpcResult.isSuccessful()) {
252                 LOG.error("RPC Call to Get Unique Id for nexthopKey {} returned with Errors {}",
253                         nexthopKey, rpcResult.getErrors());
254             }
255         } catch (InterruptedException | ExecutionException e) {
256             LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
257         }
258     }
259
260     protected List<ActionInfo> getEgressActionsForInterface(final String ifName, int actionKey,
261                                                             boolean isTunnelInterface) {
262         List<Action> actions;
263         try {
264             if (isTunnelInterface && interfaceManager.isItmDirectTunnelsEnabled()) {
265                 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
266                         itmManager.getEgressActionsForTunnel(new GetEgressActionsForTunnelInputBuilder()
267                                 .setIntfName(ifName).build()).get();
268                 if (!rpcResult.isSuccessful()) {
269                     LOG.error("RPC Call to Get egress tunnel actions for interface {} returned with Errors {}",
270                             ifName, rpcResult.getErrors());
271                     return Collections.emptyList();
272                 } else {
273                     actions = rpcResult.getResult().nonnullAction();
274                 }
275             } else {
276                 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = odlInterfaceRpcService
277                         .getEgressActionsForInterface(new GetEgressActionsForInterfaceInputBuilder()
278                                 .setIntfName(ifName).build()).get();
279                 if (!rpcResult.isSuccessful()) {
280                     LOG.error("RPC Call to Get egress vm actions for interface {} returned with Errors {}",
281                             ifName, rpcResult.getErrors());
282                     return Collections.emptyList();
283                 } else {
284                     actions = rpcResult.getResult().nonnullAction();
285                 }
286             }
287             List<ActionInfo> listActionInfo = new ArrayList<>();
288             for (Action action : actions) {
289                 actionKey = action.key().getOrder() + actionKey;
290                 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
291                     actionClass = action.getAction();
292                 if (actionClass instanceof OutputActionCase) {
293                     listActionInfo.add(new ActionOutput(actionKey,
294                         ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
295                 } else if (actionClass instanceof PushVlanActionCase) {
296                     listActionInfo.add(new ActionPushVlan(actionKey));
297                 } else if (actionClass instanceof SetFieldCase) {
298                     if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
299                         int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
300                             .getVlanId().getVlanId().getValue();
301                         listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
302                     }
303                 } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
304                     Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
305                     listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
306                 } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
307                     NxRegLoad nxRegLoad =
308                         ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
309                     listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
310                         nxRegLoad.getDst().getStart(), nxRegLoad.getDst().getEnd(),
311                         nxRegLoad.getValue().longValue()));
312                 }
313             }
314             return listActionInfo;
315         } catch (InterruptedException | ExecutionException e) {
316             LOG.warn("Exception when egress actions for interface {}", ifName, e);
317         }
318         LOG.warn("Exception when egress actions for interface {}", ifName);
319         return Collections.emptyList();
320     }
321
322     @Nullable
323     protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
324         Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
325         Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
326         try {
327             result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
328                 .setSourceDpid(srcDpId)
329                 .setDestinationDpid(dstDpId)
330                 .setTunnelType(tunType)
331                 .build());
332             RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
333             if (!rpcResult.isSuccessful()) {
334                 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
335             } else {
336                 return rpcResult.getResult().getInterfaceName();
337             }
338         } catch (InterruptedException | ExecutionException e) {
339             LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstDpId, e);
340         }
341         return null;
342     }
343
344     @Nullable
345     protected String getTunnelInterfaceName(BigInteger srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
346         .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp, Class<? extends TunnelTypeBase> tunnelType) {
347         Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
348         try {
349             LOG.debug("Trying to fetch tunnel interface name for source dpn {} destIp {} tunType {}", srcDpId,
350                     dstIp.stringValue(), tunnelType.getName());
351             result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
352                 .setSourceDpid(srcDpId)
353                 .setDestinationIp(dstIp)
354                 .setTunnelType(tunnelType)
355                 .build());
356             RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
357             if (!rpcResult.isSuccessful()) {
358                 LOG.warn("RPC Call to getTunnelInterfaceName returned with Errors {}", rpcResult.getErrors());
359             } else {
360                 return rpcResult.getResult().getInterfaceName();
361             }
362         } catch (InterruptedException | ExecutionException e) {
363             LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstIp, e);
364         }
365         return null;
366     }
367
368
369     public long getLocalNextHopGroup(long vpnId,
370             String ipNextHopAddress) {
371         long groupId = createNextHopPointer(getNextHopKey(vpnId, ipNextHopAddress));
372         if (groupId == FibConstants.INVALID_GROUP_ID) {
373             LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
374         }
375         return groupId;
376     }
377
378     public long getLocalSelectGroup(long vpnId,
379             String ipNextHopAddress) {
380         long groupId = createNextHopPointer(getLocalSelectGroupKey(vpnId, ipNextHopAddress));
381         if (groupId == FibConstants.INVALID_GROUP_ID) {
382             LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
383         }
384         return groupId;
385     }
386
387     public long createLocalNextHop(long vpnId, BigInteger dpnId, String ifName,
388                                    String primaryIpAddress, String currDestIpPrefix,
389                                    String gwMacAddress) {
390         String vpnName = fibUtil.getVpnNameFromId(vpnId);
391         if (vpnName == null) {
392             return 0;
393         }
394         String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress);
395
396         long groupId = createNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
397         if (groupId == 0) {
398             LOG.error("Unable to allocate groupId for vpnId {} , IntfName {}, primaryIpAddress {} curIpPrefix {}",
399                     vpnId, ifName, primaryIpAddress, currDestIpPrefix);
400             return groupId;
401         }
402         String nextHopLockStr = vpnId + primaryIpAddress;
403         String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, currDestIpPrefix);
404         jobCoordinator.enqueueJob(jobKey, () -> {
405             try {
406                 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
407                     VpnNexthop nexthop = getVpnNexthop(vpnId, primaryIpAddress);
408                     LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
409                             primaryIpAddress, ifName, dpnId);
410                     if (nexthop == null) {
411                         String encMacAddress = macAddress == null
412                                 ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress) : macAddress;
413                         List<BucketInfo> listBucketInfo = new ArrayList<>();
414                         List<ActionInfo> listActionInfo = new ArrayList<>();
415                         int actionKey = 0;
416                         // MAC re-write
417                         if (encMacAddress != null) {
418                             if (gwMacAddress != null) {
419                                 LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
420                                         gwMacAddress, ifName, vpnId);
421                                 listActionInfo.add(new ActionSetFieldEthernetSource(actionKey++,
422                                         new MacAddress(gwMacAddress)));
423                             }
424                             listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey++,
425                                     new MacAddress(encMacAddress)));
426                             // listActionInfo.add(0, new ActionPopMpls());
427                         } else {
428                             // FIXME: Log message here.
429                             LOG.debug("mac address for new local nexthop is null");
430                         }
431                         listActionInfo.addAll(getEgressActionsForInterface(ifName, actionKey, false));
432                         BucketInfo bucket = new BucketInfo(listActionInfo);
433
434                         listBucketInfo.add(bucket);
435                         GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, primaryIpAddress,
436                                 GroupTypes.GroupAll, listBucketInfo);
437                         LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
438                                 encMacAddress, ifName, primaryIpAddress);
439                         //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
440                         // group to get installed before programming the flows
441                         installGroupOnDpn(groupId, dpnId, primaryIpAddress, listBucketInfo,
442                                 getNextHopKey(vpnId, primaryIpAddress), GroupTypes.GroupAll);
443                         // install Group
444                         mdsalApiManager.syncInstallGroup(groupEntity);
445                         // update MD-SAL DS
446                         addVpnNexthopToDS(dpnId, vpnId, primaryIpAddress, currDestIpPrefix, groupId);
447
448                     } else {
449                         // Ignore adding new prefix , if it already exists
450                         List<IpAdjacencies> prefixesList = nexthop.getIpAdjacencies();
451                         IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
452                         if (prefixesList != null && prefixesList.contains(prefix)) {
453                             LOG.trace("Prefix {} is already present in l3nextHop {} ", currDestIpPrefix, nexthop);
454                         } else {
455                             IpAdjacenciesBuilder ipPrefixesBuilder =
456                                     new IpAdjacenciesBuilder().withKey(new IpAdjacenciesKey(currDestIpPrefix));
457                             LOG.trace("Updating prefix {} to vpnNextHop {} Operational DS", currDestIpPrefix, nexthop);
458                             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
459                                     getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix),
460                                     ipPrefixesBuilder.build());
461                         }
462                     }
463                 }
464             } finally {
465                 FibUtil.unlockCluster(lockManager, nextHopLockStr);
466             }
467             return Collections.emptyList();
468         });
469         return groupId;
470     }
471
472     private void installGroupOnDpn(long groupId, BigInteger dpnId, String groupName, List<BucketInfo> bucketsInfo,
473                                      String nextHopKey, GroupTypes groupType) {
474         NodeRef nodeRef = FibUtil.buildNodeRef(dpnId);
475         Buckets buckets = FibUtil.buildBuckets(bucketsInfo);
476         GroupRef groupRef = new GroupRef(FibUtil.buildGroupInstanceIdentifier(groupId, dpnId));
477         AddGroupInput input = new AddGroupInputBuilder().setNode(nodeRef).setGroupId(new GroupId(groupId))
478                 .setBuckets(buckets).setGroupRef(groupRef).setGroupType(groupType)
479                 .setGroupName(groupName).build();
480         Future<RpcResult<AddGroupOutput>> groupStats = salGroupService.addGroup(input);
481         RpcResult<AddGroupOutput> rpcResult = null;
482         try {
483             rpcResult = groupStats.get();
484             if (rpcResult != null && rpcResult.isSuccessful()) {
485                 LOG.info("Group {} with key {} has been successfully installed directly on dpn {}.", groupId,
486                         nextHopKey, dpnId);
487             } else {
488                 LOG.error("Unable to install group {} with key {} directly on dpn {} due to {}.", groupId, nextHopKey,
489                         dpnId, rpcResult != null ? rpcResult.getErrors() : null);
490             }
491         } catch (InterruptedException | ExecutionException e) {
492             LOG.error("Error while installing group {} directly on dpn {}", groupId, dpnId);
493         }
494     }
495
496     protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String primaryIpAddr,
497                                      String currIpAddr, long egressPointer) {
498         InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
499             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
500
501         List<IpAdjacencies> ipPrefixesList = new ArrayList<>();
502         IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currIpAddr).build();
503         ipPrefixesList.add(prefix);
504         // Add nexthop to vpn node
505         VpnNexthop nh = new VpnNexthopBuilder()
506             .withKey(new VpnNexthopKey(primaryIpAddr))
507             .setDpnId(dpnId)
508             .setIpAdjacencies(ipPrefixesList)
509             .setEgressPointer(egressPointer).build();
510
511         InstanceIdentifier<VpnNexthop> id1 = idBuilder
512             .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddr)).build();
513         LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
514         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id1, nh);
515
516     }
517
518     protected InstanceIdentifier<IpAdjacencies> getVpnNextHopIpPrefixIdentifier(long vpnId, String primaryIpAddress,
519                                                                                 String ipPrefix) {
520         InstanceIdentifier<IpAdjacencies> id = InstanceIdentifier.builder(L3nexthop.class)
521                 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
522                 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddress))
523                 .child(IpAdjacencies.class, new IpAdjacenciesKey(ipPrefix)).build();
524         return id;
525     }
526
527     @Nullable
528     protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
529
530         // check if vpn node is there
531         InstanceIdentifierBuilder<VpnNexthops> idBuilder =
532             InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
533                 new VpnNexthopsKey(vpnId));
534         InstanceIdentifier<VpnNexthops> id = idBuilder.build();
535         Optional<VpnNexthops> vpnNexthops = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
536         if (vpnNexthops.isPresent()) {
537             // get nexthops list for vpn
538             List<VpnNexthop> nexthops = vpnNexthops.get().nonnullVpnNexthop();
539             for (VpnNexthop nexthop : nexthops) {
540                 if (Objects.equals(nexthop.getIpAddress(), ipAddress)) {
541                     // return nexthop
542                     LOG.trace("VpnNextHop : {}", nexthop);
543                     return nexthop;
544                 }
545             }
546             // return null if not found
547         }
548         return null;
549     }
550
551     @Nullable
552     public AdjacencyResult getRemoteNextHopPointer(BigInteger remoteDpnId, long vpnId, String prefixIp,
553             @Nullable String nextHopIp, Class<? extends TunnelTypeBase> tunnelType) {
554         String egressIfName = null;
555         LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
556             vpnId, prefixIp, nextHopIp);
557
558         Class<? extends InterfaceType> egressIfType;
559         ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
560         if (elanInstance != null) {
561             egressIfType = getInterfaceType(elanInstance);
562         } else {
563             LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
564             egressIfType = Tunnel.class;
565         }
566
567         if (Tunnel.class.equals(egressIfType)) {
568             egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp, tunnelType);
569         } else {
570             egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
571         }
572
573         LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
574             egressIfName);
575         return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType, nextHopIp,
576                 prefixIp) : null;
577     }
578
579     private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
580
581         InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
582             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
583             .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
584         InstanceIdentifier<VpnNexthop> id = idBuilder.build();
585         // remove from DS
586         LOG.trace("Removing vpn next hop from datastore : {}", id);
587         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
588     }
589
590     public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String primaryIpAddress, String currDestIpPrefix) {
591         String nextHopLockStr = vpnId + primaryIpAddress;
592         try {
593             if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
594                 VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
595                 if (nh != null) {
596                     List<IpAdjacencies> prefixesList = new ArrayList<>(nh.nonnullIpAdjacencies());
597                     IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
598                     prefixesList.remove(prefix);
599                     if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
600                         GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer(),
601                                 primaryIpAddress, GroupTypes.GroupAll, Collections.emptyList());
602                         // remove Group ...
603                         mdsalApiManager.removeGroup(groupEntity);
604                         //update MD-SAL DS
605                         removeVpnNexthopFromDS(vpnId, primaryIpAddress);
606                         //release groupId
607                         removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
608                         LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
609                                 nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
610                     } else {
611                         //remove the currIpPrefx from IpPrefixList of the vpnNexthop
612                         LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
613                         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
614                                 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
615                     }
616                 } else {
617                     //throw error
618                     LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
619                             vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
620                 }
621             }
622         } finally {
623             FibUtil.unlockCluster(lockManager, nextHopLockStr);
624         }
625     }
626
627     public void setConfTransType(String service, String transportType) {
628
629         if (!service.equalsIgnoreCase("L3VPN")) {
630             LOG.error("Incorrect service {} provided for setting the transport type.", service);
631             return;
632         }
633
634         L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType
635                 .toUpperCase(Locale.getDefault()));
636
637         if (transType != L3VPNTransportTypes.Invalid) {
638             configuredTransportTypeL3VPN = transType;
639         }
640     }
641
642     public void writeConfTransTypeConfigDS() {
643         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
644             createConfTransportType(configuredTransportTypeL3VPN.getTransportType()));
645     }
646
647     public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
648         return this.configuredTransportTypeL3VPN;
649     }
650
651     public String getReqTransType() {
652         if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
653             /*
654              * Restart scenario, Read from the ConfigDS.
655              * if the value is Unset, cache value as VxLAN.
656              */
657             LOG.trace("configureTransportType is not yet set.");
658             Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
659                 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
660
661             if (configuredTransTypeFromConfig.isPresent()) {
662                 if (TunnelTypeGre.class.equals(configuredTransTypeFromConfig.get().getTransportType())) {
663                     configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
664                 } else {
665                     configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
666                 }
667                 LOG.trace("configuredTransportType set from config DS to {}",
668                     getConfiguredTransportTypeL3VPN().getTransportType());
669             } else {
670                 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
671                 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
672             }
673         } else {
674             LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
675         }
676         return getConfiguredTransportTypeL3VPN().getTransportType();
677     }
678
679     public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
680         return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
681     }
682
683     private ConfTransportTypeL3vpn createConfTransportType(String type) {
684         ConfTransportTypeL3vpn confTransType;
685         switch (type) {
686             case ITMConstants.TUNNEL_TYPE_GRE:
687                 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
688                 LOG.trace("Setting the confTransportType to GRE.");
689                 break;
690             case ITMConstants.TUNNEL_TYPE_VXLAN:
691                 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
692                 LOG.trace("Setting the confTransportType to VxLAN.");
693                 break;
694             default:
695                 LOG.trace("Invalid transport type {} passed to Config DS ", type);
696                 confTransType = null;
697                 break;
698         }
699         return confTransType;
700     }
701
702     public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
703         switch (transportType) {
704             case "VXLAN":
705                 return TunnelTypeVxlan.class;
706             case "GRE":
707                 return TunnelTypeGre.class;
708             default:
709                 return TunnelTypeMplsOverGre.class;
710         }
711     }
712
713     public String getTransportTypeStr(String tunType) {
714         if (tunType.equals(TunnelTypeVxlan.class.toString())) {
715             return ITMConstants.TUNNEL_TYPE_VXLAN;
716         } else if (tunType.equals(TunnelTypeGre.class.toString())) {
717             return ITMConstants.TUNNEL_TYPE_GRE;
718         } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
719             return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
720         } else {
721             return ITMConstants.TUNNEL_TYPE_INVALID;
722         }
723     }
724
725     @Override
726     @PreDestroy
727     public void close() {
728         LOG.info("{} close", getClass().getSimpleName());
729     }
730
731     // TODO Clean up the exception handling
732     @SuppressWarnings("checkstyle:IllegalCatch")
733     @Nullable
734     private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp,
735                                                  Class<? extends TunnelTypeBase> tunnelType) {
736         if (nextHopIp != null && !nextHopIp.isEmpty()) {
737             try {
738                 // here use the config for tunnel type param
739                 return getTunnelInterfaceName(remoteDpnId,
740                     org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
741                         .getDefaultInstance(nextHopIp), tunnelType);
742             } catch (Exception ex) {
743                 LOG.error("Error while retrieving nexthop pointer for nexthop {} remoteDpn {}",
744                         nextHopIp, remoteDpnId, ex);
745             }
746         }
747
748         return null;
749     }
750
751     private String getExtPortRemoteNextHopPointer(BigInteger remoteDpnId, ElanInstance elanInstance) {
752         return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
753     }
754
755     /**
756      * Get the interface type associated with the type of ELAN used for routing
757      * traffic to/from remote compute nodes.
758      *
759      * @param elanInstance The elan instance
760      * @return L2vlan for flat/VLAN network type and Tunnel otherwise
761      */
762     private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
763         Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
764         if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
765             return L2vlan.class;
766         }
767
768         return Tunnel.class;
769     }
770
771     private ElanInstance getElanInstanceForPrefix(long vpnId, String prefixIp) {
772         ElanInstance elanInstance = null;
773         Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, prefixIp);
774         if (prefix != null) {
775             if (prefix.getNetworkId() != null) {
776                 elanInstance = elanService.getElanInstance(prefix.getNetworkId().getValue());
777             }
778         }
779
780         return elanInstance;
781     }
782
783     static class AdjacencyResult {
784         private final String interfaceName;
785         private final Class<? extends InterfaceType> interfaceType;
786         private final String nextHopIp;
787         private final String prefix;
788
789         AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType, String nextHopIp,
790                         String prefix) {
791             this.interfaceName = interfaceName;
792             this.interfaceType = interfaceType;
793             this.nextHopIp = nextHopIp;
794             this.prefix = prefix;
795         }
796
797         public String getInterfaceName() {
798             return interfaceName;
799         }
800
801         public Class<? extends InterfaceType> getInterfaceType() {
802             return interfaceType;
803         }
804
805         public String getNextHopIp() {
806             return nextHopIp;
807         }
808
809         public String getPrefix() {
810             return prefix;
811         }
812
813         @Override
814         public int hashCode() {
815             final int prime = 31;
816             int result = 1;
817             result = prime * result + (interfaceName == null ? 0 : interfaceName.hashCode());
818             return result;
819         }
820
821         @Override
822         public boolean equals(Object obj) {
823             if (obj == null) {
824                 return false;
825             }
826
827             if (getClass() != obj.getClass()) {
828                 return false;
829             } else {
830                 AdjacencyResult other = (AdjacencyResult) obj;
831                 return interfaceName.equals(other.interfaceName);
832             }
833         }
834     }
835
836     protected long setupLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId,
837             String destPrefix, List<BucketInfo> localBucketInfo, List<BucketInfo> remoteBucketInfo) {
838         long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
839         if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
840             LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
841             return remoteGroupId;
842         }
843         long localGroupId =  createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
844         if (localGroupId == FibConstants.INVALID_GROUP_ID) {
845             LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}",
846                 parentVpnId, destPrefix);
847             return remoteGroupId;
848         }
849         List<BucketInfo> combinedBucketInfo = new ArrayList<>();
850         combinedBucketInfo.addAll(localBucketInfo);
851         combinedBucketInfo.addAll(remoteBucketInfo);
852         GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
853                 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, combinedBucketInfo);
854         GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
855                 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, localBucketInfo);
856         String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
857         jobCoordinator.enqueueJob(jobKey, () -> {
858             mdsalApiManager.syncInstallGroup(remoteGroupEntity);
859             if (!localBucketInfo.isEmpty()) {
860                 mdsalApiManager.syncInstallGroup(localGroupEntity);
861             }
862             if (LOG.isDebugEnabled()) {
863                 LOG.debug("Finished installing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
864                         + "localGroupEntity.groupId {}  groupEntity.groupType {}", jobKey,
865                         remoteGroupEntity.getGroupId(), localGroupEntity.getGroupId(),
866                         remoteGroupEntity.getGroupType());
867             }
868             // Delete local group(if exists) if there is no local info.
869             // Local group has to be deleted if all VMs in a compute is deleted.
870             if (localBucketInfo.isEmpty()) {
871                 LOG.debug("Deleting local group {} since no local nhs present for "
872                         + "prefix {}", localGroupEntity.getGroupId(), destPrefix);
873                 mdsalApiManager.syncRemoveGroup(localGroupEntity);
874             }
875             return Collections.emptyList();
876         });
877         return remoteGroupId;
878     }
879
880     protected void deleteLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId, String destPrefix) {
881         long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
882         if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
883             LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
884         }
885         long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
886         if (localGroupId == FibConstants.INVALID_GROUP_ID) {
887             LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
888         }
889         GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
890                 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
891         GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
892                 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
893         String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
894         jobCoordinator.enqueueJob(jobKey, () -> {
895             mdsalApiManager.syncRemoveGroup(remoteGroupEntity);
896             mdsalApiManager.syncRemoveGroup(localGroupEntity);
897             if (LOG.isDebugEnabled()) {
898                 LOG.debug("Finished removing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
899                     + "localGroupEntity.groupId {}", jobKey, remoteGroupId, localGroupId);
900             }
901             return Collections.emptyList();
902         });
903     }
904
905     long createNextHopGroups(Long vpnId, String rd, BigInteger dpnId, VrfEntry vrfEntry,
906             @Nullable Routes routes, List<Routes> vpnExtraRoutes) {
907         List<BucketInfo> localBucketInfo = new ArrayList<>();
908         List<Routes> clonedVpnExtraRoutes  = new ArrayList<>(vpnExtraRoutes);
909         if (clonedVpnExtraRoutes.contains(routes)) {
910             localBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
911             clonedVpnExtraRoutes.remove(routes);
912         }
913         List<BucketInfo> remoteBucketInfo =
914             new ArrayList<>(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
915         return setupLoadBalancingNextHop(vpnId, dpnId,
916             vrfEntry.getDestPrefix(), localBucketInfo, remoteBucketInfo);
917     }
918
919     private List<BucketInfo> getBucketsForLocalNexthop(Long vpnId, BigInteger dpnId,
920             VrfEntry vrfEntry, Routes routes) {
921         @Nullable List<String> nexthopIpList = routes.getNexthopIpList();
922         if (LOG.isDebugEnabled()) {
923             LOG.debug("NexthopManager.getBucketsForLocalNexthop invoked with vpnId {} dpnId {} "
924                             + " vrfEntry.routePaths {}, routes.nexthopList {}", vpnId, dpnId, vrfEntry.getRoutePaths(),
925                 nexthopIpList);
926         }
927         List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
928         if (nexthopIpList != null) {
929             nexthopIpList.parallelStream().forEach(nextHopIp -> {
930                 String localNextHopIP;
931                 if (isIpv4Address(nextHopIp)) {
932                     localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
933                 } else {
934                     localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
935                 }
936                 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
937                 if (localNextHopInfo != null) {
938                     long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
939                     if (groupId == FibConstants.INVALID_GROUP_ID) {
940                         LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
941                             vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
942                         return;
943                     }
944                     List<ActionInfo> actionsInfos =
945                         Collections.singletonList(new ActionGroup(groupId));
946                     BucketInfo bucket = new BucketInfo(actionsInfos);
947                     bucket.setWeight(1);
948                     listBucketInfo.add(bucket);
949                 }
950             });
951         }
952         LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
953         return listBucketInfo;
954     }
955
956     private List<BucketInfo> getBucketsForRemoteNexthop(Long vpnId, BigInteger dpnId, VrfEntry vrfEntry, String rd,
957             List<Routes> vpnExtraRoutes) {
958         List<BucketInfo> listBucketInfo = new ArrayList<>();
959         Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
960         vpnExtraRoutes.stream().filter(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList() != null).forEach(
961             vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
962                 String nextHopPrefixIp;
963                 if (isIpv4Address(nextHopIp)) {
964                     nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
965                 } else {
966                     nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
967                 }
968                 List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
969                 if (tepIpAddresses.isEmpty()) {
970                     return;
971                 }
972                 // There would be only one nexthop address for a VM ip which would be the tep Ip
973                 String tepIp = tepIpAddresses.get(0);
974                 AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
975                     vrfEntry.getDestPrefix(), tepIp, TunnelTypeVxlan.class);
976                 if (adjacencyResult == null) {
977                     return;
978                 }
979                 String egressInterface = adjacencyResult.getInterfaceName();
980                 if (!FibUtil.isTunnelInterface(adjacencyResult)) {
981                     return;
982                 }
983                 Class<? extends TunnelTypeBase> tunnelType =
984                     VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
985                 StateTunnelList ifState = null;
986                 try {
987                     ifState = fibUtil.getTunnelState(egressInterface);
988                     if (ifState == null || ifState.getOperState() != TunnelOperStatus.Up) {
989                         LOG.trace("Tunnel is not up for interface {}", egressInterface);
990                         return;
991                     }
992                 } catch (ReadFailedException e) {
993                     LOG.error("getBucketsForRemoteNexthop: error in fetching tunnel state for interface {}",
994                         egressInterface, e);
995                     return;
996                 }
997                 if (!TunnelTypeVxlan.class.equals(tunnelType)) {
998                     return;
999                 }
1000                 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
1001                 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
1002                 if (prefixInfo == null) {
1003                     LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
1004                         vpnId);
1005                     return;
1006                 }
1007                 BigInteger tunnelId;
1008                 if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
1009                     tunnelId = BigInteger.valueOf(prefixInfo.getSegmentationId());
1010                 } else {
1011                     LOG.warn("Network is not of type VXLAN for prefix {}."
1012                         + "Going with default Lport Tag.", prefixInfo.toString());
1013                     tunnelId = BigInteger.valueOf(label);
1014                 }
1015                 List<ActionInfo> actionInfos = new ArrayList<>();
1016                 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
1017                 String ifName = prefixInfo.getVpnInterfaceName();
1018                 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1019                 if (vpnName == null) {
1020                     return;
1021                 }
1022                 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
1023                 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
1024                     new MacAddress(macAddress)));
1025                 List<ActionInfo> egressActions;
1026                 if (egressActionMap.containsKey(egressInterface)) {
1027                     egressActions = egressActionMap.get(egressInterface);
1028                 } else {
1029                     egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(), true);
1030                     egressActionMap.put(egressInterface, egressActions);
1031                 }
1032                 if (egressActions.isEmpty()) {
1033                     LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
1034                             + " interface {}." + " Aborting remote FIB entry creation.",
1035                         vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
1036                 }
1037                 actionInfos.addAll(egressActions);
1038                 BucketInfo bucket = new BucketInfo(actionInfos);
1039                 bucket.setWeight(1);
1040                 listBucketInfo.add(bucket);
1041             }));
1042         LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
1043         return listBucketInfo;
1044     }
1045
1046     public void createDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1047                                              Class<? extends TunnelTypeBase> tunnelType) {
1048         Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1049         int noOfDcGws = availableDcGws.size();
1050         if (noOfDcGws == 1) {
1051             LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
1052             return;
1053         }
1054         // TODO : Place the logic to construct all possible DC-GW combination here.
1055         String groupIdKey = FibUtil.getGreLbGroupKey(availableDcGws);
1056         Long groupId = createNextHopPointer(groupIdKey);
1057         List<Bucket> listBucket = new ArrayList<>();
1058         for (int index = 0; index < noOfDcGws; index++) {
1059             if (isTunnelUp(availableDcGws.get(index), dpnId, tunnelType)) {
1060                 listBucket.add(buildBucketForDcGwLbGroup(availableDcGws.get(index), dpnId, index, tunnelType));
1061             }
1062         }
1063         Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
1064                         MDSALUtil.buildBucketLists(listBucket));
1065         ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1066             confTx -> mdsalApiManager.addGroup(confTx, dpnId, group)), LOG, "Error adding load-balancing group");
1067         ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
1068             operTx -> FibUtil.updateLbGroupInfo(dpnId, destinationIp, groupIdKey, groupId.toString(), operTx)), LOG,
1069             "Error updating load-balancing group info");
1070         LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}", groupIdKey, dpnId, group);
1071     }
1072
1073     private boolean isTunnelUp(String dcGwIp, BigInteger dpnId, Class<? extends TunnelTypeBase> tunnelType) {
1074         String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp, tunnelType);
1075         if (tunnelName != null) {
1076             InstanceIdentifier<StateTunnelList> tunnelStateId =
1077                     InstanceIdentifier.builder(TunnelsState.class).child(
1078                             StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
1079             return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
1080                     .toJavaUtil().map(StateTunnelList::getOperState)
1081                     .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
1082         }
1083         return false;
1084     }
1085
1086     private List<Action> getEgressActions(String interfaceName, int actionKey) {
1087         List<Action> actions = Collections.emptyList();
1088         try {
1089             GetEgressActionsForInterfaceInputBuilder egressAction =
1090                     new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
1091             Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1092                     odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
1093             RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1094             if (!rpcResult.isSuccessful()) {
1095                 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1096                         interfaceName, rpcResult.getErrors());
1097             } else {
1098                 actions = rpcResult.getResult().nonnullAction();
1099             }
1100         } catch (InterruptedException | ExecutionException e) {
1101             LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1102         }
1103         return actions;
1104     }
1105
1106     /**
1107      * This method is invoked when the tunnel state is removed from DS.
1108      * If the there is just one DC-GW left in configuration then the LB groups can be deleted.
1109      * Otherwise, the groups are just updated.
1110      */
1111     public void removeOrUpdateDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
1112             String destinationIp) {
1113         Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1114         ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1115             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, operTx -> {
1116                 int noOfDcGws = availableDcGws.size();
1117                 // If availableDcGws does not contain the destination Ip it means this is a configuration delete.
1118                 if (!availableDcGws.contains(destinationIp)) {
1119                     availableDcGws.add(destinationIp);
1120                     Collections.sort(availableDcGws);
1121                 }
1122                 // TODO : Place the logic to construct all possible DC-GW combination here.
1123                 int bucketId = availableDcGws.indexOf(destinationIp);
1124                 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1125                 if (!dpnLbNextHops.isPresent()) {
1126                     return;
1127                 }
1128                 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1129                 if (nextHopKeys != null) {
1130                     for (String nextHopKey : nextHopKeys) {
1131                         Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1132                         if (!optionalNextHops.isPresent()) {
1133                             return;
1134                         }
1135                         Nexthops nexthops = optionalNextHops.get();
1136                         final String groupId = nexthops.getGroupId();
1137                         final long groupIdValue = Long.parseLong(groupId);
1138                         if (noOfDcGws > 1) {
1139                             mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1140                         } else {
1141                             LOG.trace("Removed LB group {} on dpn {}", groupIdValue, dpnId);
1142                             mdsalApiManager.removeGroup(confTx, dpnId, groupIdValue);
1143                             removeNextHopPointer(nextHopKey);
1144                         }
1145                         // When the DC-GW is removed from configuration.
1146                         if (noOfDcGws != availableDcGws.size()) {
1147                             FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operTx);
1148                         }
1149                     }
1150                 }
1151                 FibUtil.removeDpnIdToNextHopInfo(destinationIp, dpnId, operTx);
1152             }), LOG, "Error removing or updating load-balancing group");
1153         }), LOG, "Error removing or updating load-balancing group");
1154     }
1155
1156     /**
1157      * This method is invoked when the tunnel status is updated.
1158      * The bucket is directly removed/added based on the operational status of the tunnel.
1159      */
1160     public void updateDcGwLoadBalancingGroup(List<String> availableDcGws,
1161             BigInteger dpnId, String destinationIp, boolean isTunnelUp, Class<? extends TunnelTypeBase> tunnelType) {
1162         Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1163         ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1164             // TODO : Place the logic to construct all possible DC-GW combination here.
1165             int bucketId = availableDcGws.indexOf(destinationIp);
1166             Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1167             if (!dpnLbNextHops.isPresent()) {
1168                 return;
1169             }
1170             List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1171             if (nextHopKeys != null) {
1172                 for (String nextHopKey : nextHopKeys) {
1173                     Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1174                     if (!optionalNextHops.isPresent()) {
1175                         return;
1176                     }
1177                     Nexthops nexthops = optionalNextHops.get();
1178                     final String groupId = nexthops.getGroupId();
1179                     final long groupIdValue = Long.parseLong(groupId);
1180                     if (isTunnelUp) {
1181                         Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId, tunnelType);
1182                         LOG.trace("Added bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
1183                         mdsalApiManager.addBucket(confTx, dpnId, groupIdValue, bucket);
1184                     } else {
1185                         LOG.trace("Removed bucketId {} from group {} on dpn {}.", bucketId, groupId, dpnId);
1186                         mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1187                     }
1188                 }
1189             }
1190         }), LOG, "Error updating load-balancing group");
1191     }
1192
1193     private Bucket buildBucketForDcGwLbGroup(String ipAddress, BigInteger dpnId, int index,
1194                                              Class<? extends TunnelTypeBase> tunnelType) {
1195         List<Action> listAction = new ArrayList<>();
1196         // ActionKey 0 goes to mpls label.
1197         int actionKey = 1;
1198         listAction.add(new ActionPushMpls().buildAction());
1199         listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
1200                 .get(index), 0, 19).buildAction());
1201         String tunnelInterfaceName = getTunnelInterfaceName(dpnId, IpAddressBuilder.getDefaultInstance(ipAddress),
1202             tunnelType);
1203         List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
1204         if (!egressActions.isEmpty()) {
1205             listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
1206         } else {
1207             // clear off actions if there is no egress actions.
1208             listAction = Collections.emptyList();
1209         }
1210         //OVS expects a non-zero weight value for load balancing to happen in select groups
1211         return MDSALUtil.buildBucket(listAction, SELECT_GROUP_WEIGHT, index,
1212                 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
1213     }
1214
1215     public void programDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1216                                               int addRemoveOrUpdate, boolean isTunnelUp,
1217                                               Class<? extends TunnelTypeBase> tunnelType) {
1218         if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
1219             createDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, tunnelType);
1220         } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
1221             removeOrUpdateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
1222         } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
1223             updateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, isTunnelUp, tunnelType);
1224         }
1225     }
1226 }