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