Clean up lambdas
[netvirt.git] / vpnservice / fibmanager / fibmanager-impl / src / main / java / org / opendaylight / netvirt / fibmanager / NexthopManager.java
1 /*
2  * Copyright (c) 2015 - 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netvirt.fibmanager;
9
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.CheckedFuture;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.Future;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
21 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
24 import org.opendaylight.genius.itm.globals.ITMConstants;
25 import org.opendaylight.genius.mdsalutil.ActionInfo;
26 import org.opendaylight.genius.mdsalutil.BucketInfo;
27 import org.opendaylight.genius.mdsalutil.GroupEntity;
28 import org.opendaylight.genius.mdsalutil.MDSALUtil;
29 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
30 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
31 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
32 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
33 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
34 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
35 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
36 import org.opendaylight.netvirt.elanmanager.api.IElanService;
37 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
38 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
39 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameInputBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameOutput;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVlan;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpn;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpnBuilder;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
88 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;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
90 import org.opendaylight.yangtools.yang.binding.DataObject;
91 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
92 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
93 import org.opendaylight.yangtools.yang.common.RpcResult;
94 import org.slf4j.Logger;
95 import org.slf4j.LoggerFactory;
96
97 public class NexthopManager implements AutoCloseable {
98     private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
99     private final DataBroker dataBroker;
100     private final IMdsalApiManager mdsalApiManager;
101     private final OdlInterfaceRpcService interfaceManager;
102     private final ItmRpcService itmManager;
103     private final IdManagerService idManager;
104     private final IElanService elanService;
105     private static final short LPORT_INGRESS_TABLE = 0;
106     private static final short LFIB_TABLE = 20;
107     private static final short FIB_TABLE = 21;
108     private static final short DEFAULT_FLOW_PRIORITY = 10;
109     private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
110     private static final long FIXED_DELAY_IN_MILLISECONDS = 4000;
111     private L3VPNTransportTypes configuredTransportTypeL3VPN = L3VPNTransportTypes.Invalid;
112     private Long waitTimeForSyncInstall;
113
114     private static final FutureCallback<Void> DEFAULT_CALLBACK =
115         new FutureCallback<Void>() {
116             @Override
117             public void onSuccess(Void result) {
118                 LOG.debug("Success in Datastore write operation");
119             }
120
121             @Override
122             public void onFailure(Throwable error) {
123                 LOG.error("Error in Datastore write operation", error);
124             }
125
126             ;
127         };
128
129     /**
130      * Provides nexthop functions.
131      * Creates group ID pool
132      *
133      * @param dataBroker       - dataBroker reference
134      * @param mdsalApiManager  - mdsalApiManager reference
135      * @param idManager        - idManager reference
136      * @param interfaceManager - interfaceManager reference
137      * @param itmManager       - itmManager reference
138      */
139     public NexthopManager(final DataBroker dataBroker,
140                           final IMdsalApiManager mdsalApiManager,
141                           final IdManagerService idManager,
142                           final OdlInterfaceRpcService interfaceManager,
143                           final ItmRpcService itmManager,
144                           final IElanService elanService) {
145         this.dataBroker = dataBroker;
146         this.mdsalApiManager = mdsalApiManager;
147         this.idManager = idManager;
148         this.interfaceManager = interfaceManager;
149         this.itmManager = itmManager;
150         this.elanService = elanService;
151         waitTimeForSyncInstall = Long.getLong("wait.time.sync.install");
152         if (waitTimeForSyncInstall == null) {
153             waitTimeForSyncInstall = 1000L;
154         }
155
156         createIdPool();
157     }
158
159     private void createIdPool() {
160         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
161             .setPoolName(NEXTHOP_ID_POOL_NAME)
162             .setLow(150000L)
163             .setHigh(175000L)
164             .build();
165         try {
166             Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
167             if ((result != null) && (result.get().isSuccessful())) {
168                 LOG.info("Created IdPool for NextHopPointerPool");
169             }
170         } catch (InterruptedException | ExecutionException e) {
171             LOG.error("Failed to create idPool for NextHopPointerPool", e);
172         }
173     }
174
175     private BigInteger getDpnId(String ofPortId) {
176         String[] fields = ofPortId.split(":");
177         BigInteger dpn = new BigInteger(fields[1]);
178         LOG.debug("DpnId: {}", dpn);
179         return dpn;
180     }
181
182     private String getNextHopKey(long vpnId, String ipAddress) {
183         String nhKey = new String("nexthop." + vpnId + ipAddress);
184         return nhKey;
185     }
186
187     private String getNextHopKey(String ifName, String ipAddress) {
188         String nhKey = new String("nexthop." + ifName + ipAddress);
189         return nhKey;
190     }
191
192     protected long createNextHopPointer(String nexthopKey) {
193         AllocateIdInput getIdInput = new AllocateIdInputBuilder()
194             .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
195             .build();
196         //TODO: Proper error handling once IdManager code is complete
197         try {
198             Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
199             RpcResult<AllocateIdOutput> rpcResult = result.get();
200             return rpcResult.getResult().getIdValue();
201         } catch (NullPointerException | InterruptedException | ExecutionException e) {
202             LOG.trace("", e);
203         }
204         return 0;
205     }
206
207     protected void removeNextHopPointer(String nexthopKey) {
208         ReleaseIdInput idInput = new ReleaseIdInputBuilder()
209             .setPoolName(NEXTHOP_ID_POOL_NAME)
210             .setIdKey(nexthopKey).build();
211         try {
212             Future<RpcResult<Void>> result = idManager.releaseId(idInput);
213             RpcResult<Void> rpcResult = result.get();
214             if (!rpcResult.isSuccessful()) {
215                 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
216             }
217         } catch (InterruptedException | ExecutionException e) {
218             LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
219         }
220     }
221
222     protected List<ActionInfo> getEgressActionsForInterface(String ifName) {
223         List<ActionInfo> listActionInfo = new ArrayList<>();
224         try {
225             Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
226                 interfaceManager.getEgressActionsForInterface(
227                     new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).build());
228             RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
229             if (!rpcResult.isSuccessful()) {
230                 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
231                     ifName, rpcResult.getErrors());
232             } else {
233                 List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> actions =
234                     rpcResult.getResult().getAction();
235                 for (Action action : actions) {
236                     org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
237                         actionClass = action.getAction();
238                     if (actionClass instanceof OutputActionCase) {
239                         listActionInfo.add(new ActionOutput(
240                             ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
241                     } else if (actionClass instanceof PushVlanActionCase) {
242                         listActionInfo.add(new ActionPushVlan());
243                     } else if (actionClass instanceof SetFieldCase) {
244                         if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
245                             int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
246                                 .getVlanId().getVlanId().getValue();
247                             listActionInfo.add(new ActionSetFieldVlanVid(vlanVid));
248                         }
249                     } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
250                         Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
251                         listActionInfo.add(new ActionNxResubmit(action.getKey().getOrder() + 1, tableId));
252                     } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
253                         NxRegLoad nxRegLoad =
254                             ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
255                         listActionInfo.add(new ActionRegLoad(action.getKey().getOrder() + 1, NxmNxReg6.class,
256                             nxRegLoad.getDst().getStart(), nxRegLoad.getDst().getEnd(),
257                             nxRegLoad.getValue().longValue()));
258                     }
259                 }
260             }
261         } catch (InterruptedException | ExecutionException e) {
262             LOG.warn("Exception when egress actions for interface {}", ifName, e);
263         }
264         return listActionInfo;
265     }
266
267     protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
268         Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase());
269         Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
270         try {
271             result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
272                 .setSourceDpid(srcDpId)
273                 .setDestinationDpid(dstDpId)
274                 .setTunnelType(tunType)
275                 .build());
276             RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
277             if (!rpcResult.isSuccessful()) {
278                 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
279             } else {
280                 return rpcResult.getResult().getInterfaceName();
281             }
282         } catch (InterruptedException | ExecutionException e) {
283             LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstDpId, e);
284         }
285         return null;
286     }
287
288     protected String getTunnelInterfaceName(BigInteger srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
289         .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp) {
290         Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase());
291         Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
292         try {
293             result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
294                 .setSourceDpid(srcDpId)
295                 .setDestinationIp(dstIp)
296                 .setTunnelType(tunType)
297                 .build());
298             RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
299             if (!rpcResult.isSuccessful()) {
300                 LOG.warn("RPC Call to getTunnelInterfaceName returned with Errors {}", rpcResult.getErrors());
301             } else {
302                 return rpcResult.getResult().getInterfaceName();
303             }
304         } catch (InterruptedException | ExecutionException e) {
305             LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstIp, e);
306         }
307         return null;
308     }
309
310     public long createLocalNextHop(long vpnId, BigInteger dpnId,
311                                    String ifName, String ipNextHopAddress, String ipPrefixAddress) {
312         String macAddress = FibUtil.getMacAddressFromPrefix(dataBroker, ifName, ipPrefixAddress);
313         String ipAddress = (macAddress != null) ? ipPrefixAddress : ipNextHopAddress;
314
315         long groupId = createNextHopPointer(getNextHopKey(vpnId, ipAddress));
316         if (groupId == 0) {
317             LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipAddress);
318             return groupId;
319         }
320         String nextHopLockStr = new String(vpnId + ipAddress);
321         synchronized (nextHopLockStr.intern()) {
322             VpnNexthop nexthop = getVpnNexthop(vpnId, ipAddress);
323             LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop,
324                 vpnId, ipAddress, ifName, dpnId);
325             if (nexthop == null) {
326                 if (macAddress == null) {
327                     macAddress = FibUtil.getMacAddressFromPrefix(dataBroker, ifName, ipAddress);
328                 }
329                 List<BucketInfo> listBucketInfo = new ArrayList<>();
330                 List<ActionInfo> listActionInfo = new ArrayList<>();
331                 // MAC re-write
332                 if (macAddress != null) {
333                     int actionKey = listActionInfo.size();
334                     listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey, new MacAddress(macAddress)));
335                     //listActionInfo.add(0, new ActionPopMpls());
336                 } else {
337                     //FIXME: Log message here.
338                     LOG.debug("mac address for new local nexthop is null");
339                 }
340                 listActionInfo.addAll(getEgressActionsForInterface(ifName));
341                 BucketInfo bucket = new BucketInfo(listActionInfo);
342
343                 listBucketInfo.add(bucket);
344                 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
345                     dpnId, groupId, ipAddress, GroupTypes.GroupAll, listBucketInfo);
346                 LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}",
347                     groupId, macAddress, ifName, ipAddress);
348
349                 // install Group
350                 mdsalApiManager.syncInstallGroup(groupEntity, FIXED_DELAY_IN_MILLISECONDS);
351                 try {
352                     LOG.info("Sleeping for {} to wait for the groups to get programmed.", waitTimeForSyncInstall);
353                     Thread.sleep(waitTimeForSyncInstall);
354                 } catch (InterruptedException error) {
355                     LOG.warn("Error while waiting for group {} to install.", groupId);
356                     LOG.debug("{}", error);
357                 }
358                 //update MD-SAL DS
359                 addVpnNexthopToDS(dpnId, vpnId, ipAddress, groupId);
360
361             } else {
362                 //nexthop exists already; a new flow is going to point to it, increment the flowrefCount by 1
363                 int flowrefCnt = nexthop.getFlowrefCount() + 1;
364                 VpnNexthop nh = new VpnNexthopBuilder().setKey(new VpnNexthopKey(ipAddress))
365                     .setFlowrefCount(flowrefCnt).build();
366                 LOG.trace("Updating vpnnextHop {} for refCount {} to Operational DS", nh, flowrefCnt);
367                 syncWrite(LogicalDatastoreType.OPERATIONAL, getVpnNextHopIdentifier(vpnId, ipAddress),
368                     nh, DEFAULT_CALLBACK);
369             }
370         }
371         return groupId;
372     }
373
374     protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String ipPrefix, long egressPointer) {
375         InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
376             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
377
378         // Add nexthop to vpn node
379         VpnNexthop nh = new VpnNexthopBuilder()
380             .setKey(new VpnNexthopKey(ipPrefix))
381             .setDpnId(dpnId)
382             .setIpAddress(ipPrefix)
383             .setFlowrefCount(1)
384             .setEgressPointer(egressPointer).build();
385
386         InstanceIdentifier<VpnNexthop> id1 = idBuilder
387             .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix)).build();
388         LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
389         syncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
390
391     }
392
393     protected InstanceIdentifier<VpnNexthop> getVpnNextHopIdentifier(long vpnId, String ipAddress) {
394         InstanceIdentifier<VpnNexthop> id = InstanceIdentifier.builder(L3nexthop.class)
395             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId)).child(VpnNexthop.class,
396                 new VpnNexthopKey(ipAddress)).build();
397         return id;
398     }
399
400     protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
401
402         // check if vpn node is there
403         InstanceIdentifierBuilder<VpnNexthops> idBuilder =
404             InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
405                 new VpnNexthopsKey(vpnId));
406         InstanceIdentifier<VpnNexthops> id = idBuilder.build();
407         Optional<VpnNexthops> vpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
408         if (vpnNexthops.isPresent()) {
409             // get nexthops list for vpn
410             List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
411             for (VpnNexthop nexthop : nexthops) {
412                 if (nexthop.getIpAddress().equals(ipAddress)) {
413                     // return nexthop
414                     LOG.trace("VpnNextHop : {}", nexthop);
415                     return nexthop;
416                 }
417             }
418             // return null if not found
419         }
420         return null;
421     }
422
423     public AdjacencyResult getRemoteNextHopPointer(BigInteger remoteDpnId, long vpnId, String prefixIp,
424                                                    String nextHopIp) {
425         String egressIfName = null;
426         LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
427             vpnId, prefixIp, nextHopIp);
428
429         Class<? extends InterfaceType> egressIfType;
430         ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
431         if (elanInstance != null) {
432             egressIfType = getInterfaceType(elanInstance);
433         } else {
434             LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
435             egressIfType = Tunnel.class;
436         }
437
438         if (Tunnel.class.equals(egressIfType)) {
439             egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp);
440         } else {
441             egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
442         }
443
444         LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
445             egressIfName);
446         return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType) : null;
447     }
448
449     public BigInteger getDpnForPrefix(long vpnId, String prefixIp) {
450         VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
451         BigInteger localDpnId = (vpnNexthop == null) ? null : vpnNexthop.getDpnId();
452         return localDpnId;
453     }
454
455     private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
456
457         InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
458             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
459             .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
460         InstanceIdentifier<VpnNexthop> id = idBuilder.build();
461         // remove from DS
462         LOG.trace("Removing vpn next hop from datastore : {}", id);
463         syncDelete(LogicalDatastoreType.OPERATIONAL, id);
464     }
465
466     public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String ipNextHopAddress, String ipPrefixAddress) {
467         String ipPrefixStr = new String(vpnId + ipPrefixAddress);
468         VpnNexthop prefixNh = null;
469         synchronized (ipPrefixStr.intern()) {
470             prefixNh = getVpnNexthop(vpnId, ipPrefixAddress);
471         }
472         String ipAddress = (prefixNh != null) ? ipPrefixAddress : ipNextHopAddress;
473
474         String nextHopLockStr = new String(vpnId + ipAddress);
475         synchronized (nextHopLockStr.intern()) {
476             VpnNexthop nh = getVpnNexthop(vpnId, ipAddress);
477             if (nh != null) {
478                 int newFlowrefCnt = nh.getFlowrefCount() - 1;
479                 if (newFlowrefCnt == 0) { //remove the group only if there are no more flows using this group
480                     GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
481                         dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupAll, null);
482                     // remove Group ...
483                     mdsalApiManager.removeGroup(groupEntity);
484                     //update MD-SAL DS
485                     removeVpnNexthopFromDS(vpnId, ipAddress);
486                     //release groupId
487                     removeNextHopPointer(getNextHopKey(vpnId, ipAddress));
488                     LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
489                         nh.getEgressPointer(), vpnId, ipAddress, dpnId);
490                 } else {
491                     //just update the flowrefCount of the vpnNexthop
492                     VpnNexthop currNh = new VpnNexthopBuilder().setKey(new VpnNexthopKey(ipAddress))
493                         .setFlowrefCount(newFlowrefCnt).build();
494                     LOG.trace("Updating vpnnextHop {} for refCount {} to Operational DS", currNh, newFlowrefCnt);
495                     syncWrite(LogicalDatastoreType.OPERATIONAL, getVpnNextHopIdentifier(vpnId, ipAddress), currNh,
496                         DEFAULT_CALLBACK);
497                 }
498             } else {
499                 //throw error
500                 LOG.error("Local Next hop for {} on dpn {} not deleted", ipAddress, dpnId);
501             }
502         }
503     }
504
505
506     // TODO Clean up the exception handling
507     @SuppressWarnings("checkstyle:IllegalCatch")
508     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
509                                                     InstanceIdentifier<T> path) {
510
511         ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction();
512
513         Optional<T> result = Optional.absent();
514         try {
515             result = tx.read(datastoreType, path).get();
516         } catch (Exception e) {
517             throw new RuntimeException(e);
518         }
519
520         return result;
521     }
522
523     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
524                                                    InstanceIdentifier<T> path, T data,
525                                                    FutureCallback<Void> callback) {
526         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
527         tx.merge(datastoreType, path, data, true);
528         Futures.addCallback(tx.submit(), callback);
529     }
530
531     private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
532                                                   InstanceIdentifier<T> path, T data,
533                                                   FutureCallback<Void> callback) {
534         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
535         tx.merge(datastoreType, path, data, true);
536         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
537         try {
538             futures.get();
539         } catch (InterruptedException | ExecutionException e) {
540             LOG.error("Error writing to datastore (path, data) : ({}, {})", path, data, e);
541             throw new RuntimeException(e.getMessage());
542         }
543     }
544
545     private <T extends DataObject> void syncDelete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
546         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
547         tx.delete(datastoreType, path);
548         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
549         try {
550             futures.get();
551         } catch (InterruptedException | ExecutionException e) {
552             LOG.error("Error deleting from datastore (path) : ({})", path, e);
553             throw new RuntimeException(e.getMessage());
554         }
555     }
556
557     private InstanceIdentifier<Adjacency> getAdjacencyIdentifier(String vpnInterfaceName, String ipAddress) {
558         return InstanceIdentifier.builder(VpnInterfaces.class)
559             .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).augmentation(
560                 Adjacencies.class).child(Adjacency.class, new AdjacencyKey(ipAddress)).build();
561     }
562
563     InstanceIdentifier<Adjacencies> getAdjListPath(String vpnInterfaceName) {
564         return InstanceIdentifier.builder(VpnInterfaces.class)
565             .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).augmentation(
566                 Adjacencies.class).build();
567     }
568
569     // TODO Clean up the console output
570     @SuppressWarnings("checkstyle:RegexpSinglelineJava")
571     public void setConfTransType(String service, String transportType) {
572
573         if (!service.toUpperCase().equals("L3VPN")) {
574             System.out.println("Please provide a valid service name. Available value(s): L3VPN");
575             LOG.error("Incorrect service {} provided for setting the transport type.", service);
576             return;
577         }
578
579         L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType.toUpperCase());
580
581         if (transType != L3VPNTransportTypes.Invalid) {
582             configuredTransportTypeL3VPN = transType;
583         }
584     }
585
586     public void writeConfTransTypeConfigDS() {
587         FibUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
588             createConfTransportType(configuredTransportTypeL3VPN.getTransportType()),
589             FibUtil.DEFAULT_CALLBACK);
590     }
591
592     public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
593         return this.configuredTransportTypeL3VPN;
594     }
595
596     public String getReqTransType() {
597         if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
598             /*
599             * Restart scenario, Read from the ConfigDS.
600             * if the value is Unset, cache value as VxLAN.
601             */
602             LOG.trace("configureTransportType is not yet set.");
603             Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
604                 FibUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
605
606             if (configuredTransTypeFromConfig.isPresent()) {
607                 if (configuredTransTypeFromConfig.get().getTransportType().equals(TunnelTypeGre.class)) {
608                     configuredTransportTypeL3VPN.setL3VPNTransportTypes(ITMConstants.TUNNEL_TYPE_GRE);
609                 } else {
610                     configuredTransportTypeL3VPN.setL3VPNTransportTypes(ITMConstants.TUNNEL_TYPE_VXLAN);
611                 }
612                 LOG.trace("configuredTransportType set from config DS to {}",
613                     getConfiguredTransportTypeL3VPN().getTransportType());
614             } else {
615                 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
616                 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
617             }
618         } else {
619             LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
620         }
621         return getConfiguredTransportTypeL3VPN().getTransportType();
622     }
623
624     public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
625         return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
626     }
627
628     private ConfTransportTypeL3vpn createConfTransportType(String type) {
629         ConfTransportTypeL3vpn confTransType;
630         if (type.equals(ITMConstants.TUNNEL_TYPE_GRE)) {
631             confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
632             LOG.trace("Setting the confTransportType to GRE.");
633         } else if (type.equals(ITMConstants.TUNNEL_TYPE_VXLAN)) {
634             confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
635             LOG.trace("Setting the confTransportType to VxLAN.");
636         } else {
637             LOG.trace("Invalid transport type {} passed to Config DS ", type);
638             confTransType = null;
639         }
640         return confTransType;
641     }
642
643     public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
644         if (transportType.equals("VXLAN")) {
645             return TunnelTypeVxlan.class;
646         } else if (transportType.equals("GRE")) {
647             return TunnelTypeGre.class;
648         } else {
649             return TunnelTypeMplsOverGre.class;
650         }
651     }
652
653     public String getTransportTypeStr(String tunType) {
654         if (tunType.equals(TunnelTypeVxlan.class.toString())) {
655             return ITMConstants.TUNNEL_TYPE_VXLAN;
656         } else if (tunType.equals(TunnelTypeGre.class.toString())) {
657             return ITMConstants.TUNNEL_TYPE_GRE;
658         } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
659             return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
660         } else {
661             return ITMConstants.TUNNEL_TYPE_INVALID;
662         }
663     }
664
665     @Override
666     public void close() throws Exception {
667         LOG.info("{} close", getClass().getSimpleName());
668     }
669
670     // TODO Clean up the exception handling
671     @SuppressWarnings("checkstyle:IllegalCatch")
672     private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp) {
673         if (nextHopIp != null && !nextHopIp.isEmpty()) {
674             try {
675                 // here use the config for tunnel type param
676                 return getTunnelInterfaceName(remoteDpnId,
677                     org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
678                         .getDefaultInstance(nextHopIp));
679             } catch (Exception ex) {
680                 LOG.error("Error while retrieving nexthop pointer for nexthop {} : ", nextHopIp, ex);
681             }
682         }
683
684         return null;
685     }
686
687     private String getExtPortRemoteNextHopPointer(BigInteger remoteDpnId, ElanInstance elanInstance) {
688         return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
689     }
690
691     /**
692      * Get the interface type associated with the type of ELAN used for routing
693      * traffic to/from remote compute nodes.
694      *
695      * @param elanInstance The elan instance
696      * @return L2vlan for flat/VLAN network type and Tunnel otherwise
697      */
698     private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
699         Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
700         if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
701             return L2vlan.class;
702         }
703
704         return Tunnel.class;
705     }
706
707     private ElanInstance getElanInstanceForPrefix(long vpnId, String prefixIp) {
708         Prefixes prefix = FibUtil.getPrefixToInterface(dataBroker, vpnId, prefixIp);
709         if (prefix == null) {
710             LOG.warn("No prefix info was found for VPN id {} prefix {}", vpnId, prefixIp);
711             return null;
712         }
713
714         String interfaceName = prefix.getVpnInterfaceName();
715         if (interfaceName == null) {
716             LOG.warn("No VPN interface found for VPN id {} prefix {}", vpnId, prefixIp);
717             return null;
718         }
719
720         ElanInterface elanInterface = elanService.getElanInterfaceByElanInterfaceName(interfaceName);
721         if (elanInterface == null) {
722             LOG.warn("No ELAN interface found for VPN interface {} on VPN id {}", interfaceName, vpnId);
723             return null;
724         }
725
726         return elanService.getElanInstance(elanInterface.getElanInstanceName());
727     }
728
729     static class AdjacencyResult {
730         private String interfaceName;
731         private Class<? extends InterfaceType> interfaceType;
732
733         AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType) {
734             this.interfaceName = interfaceName;
735             this.interfaceType = interfaceType;
736         }
737
738         public String getInterfaceName() {
739             return interfaceName;
740         }
741
742         public Class<? extends InterfaceType> getInterfaceType() {
743             return interfaceType;
744         }
745
746         @Override
747         public int hashCode() {
748             final int prime = 31;
749             int result = 1;
750             result = prime * result + ((interfaceName == null) ? 0 : interfaceName.hashCode());
751             return result;
752         }
753
754         @Override
755         public boolean equals(Object obj) {
756             boolean result = false;
757             if (getClass() != obj.getClass()) {
758                 return result;
759             } else {
760                 AdjacencyResult other = (AdjacencyResult) obj;
761                 result = interfaceName.equals(other.interfaceName);
762             }
763             return result;
764         }
765     }
766 }