a1f13b0d90c8e4381adfbb5af21c2f5082b5192a
[netvirt.git] /
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 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         return "nexthop." + vpnId + ipAddress;
184     }
185
186     private String getNextHopKey(String ifName, String ipAddress) {
187         return "nexthop." + ifName + ipAddress;
188     }
189
190     protected long createNextHopPointer(String nexthopKey) {
191         AllocateIdInput getIdInput = new AllocateIdInputBuilder()
192             .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
193             .build();
194         //TODO: Proper error handling once IdManager code is complete
195         try {
196             Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
197             RpcResult<AllocateIdOutput> rpcResult = result.get();
198             return rpcResult.getResult().getIdValue();
199         } catch (NullPointerException | InterruptedException | ExecutionException e) {
200             LOG.trace("", e);
201         }
202         return 0;
203     }
204
205     protected void removeNextHopPointer(String nexthopKey) {
206         ReleaseIdInput idInput = new ReleaseIdInputBuilder()
207             .setPoolName(NEXTHOP_ID_POOL_NAME)
208             .setIdKey(nexthopKey).build();
209         try {
210             Future<RpcResult<Void>> result = idManager.releaseId(idInput);
211             RpcResult<Void> rpcResult = result.get();
212             if (!rpcResult.isSuccessful()) {
213                 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
214             }
215         } catch (InterruptedException | ExecutionException e) {
216             LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
217         }
218     }
219
220     protected List<ActionInfo> getEgressActionsForInterface(String ifName) {
221         List<ActionInfo> listActionInfo = new ArrayList<>();
222         try {
223             Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
224                 interfaceManager.getEgressActionsForInterface(
225                     new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).build());
226             RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
227             if (!rpcResult.isSuccessful()) {
228                 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
229                     ifName, rpcResult.getErrors());
230             } else {
231                 List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> actions =
232                     rpcResult.getResult().getAction();
233                 for (Action action : actions) {
234                     org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
235                         actionClass = action.getAction();
236                     if (actionClass instanceof OutputActionCase) {
237                         listActionInfo.add(new ActionOutput(
238                             ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
239                     } else if (actionClass instanceof PushVlanActionCase) {
240                         listActionInfo.add(new ActionPushVlan());
241                     } else if (actionClass instanceof SetFieldCase) {
242                         if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
243                             int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
244                                 .getVlanId().getVlanId().getValue();
245                             listActionInfo.add(new ActionSetFieldVlanVid(vlanVid));
246                         }
247                     } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
248                         Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
249                         listActionInfo.add(new ActionNxResubmit(action.getKey().getOrder() + 1, tableId));
250                     } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
251                         NxRegLoad nxRegLoad =
252                             ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
253                         listActionInfo.add(new ActionRegLoad(action.getKey().getOrder() + 1, NxmNxReg6.class,
254                             nxRegLoad.getDst().getStart(), nxRegLoad.getDst().getEnd(),
255                             nxRegLoad.getValue().longValue()));
256                     }
257                 }
258             }
259         } catch (InterruptedException | ExecutionException e) {
260             LOG.warn("Exception when egress actions for interface {}", ifName, e);
261         }
262         return listActionInfo;
263     }
264
265     protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
266         Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase());
267         Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
268         try {
269             result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
270                 .setSourceDpid(srcDpId)
271                 .setDestinationDpid(dstDpId)
272                 .setTunnelType(tunType)
273                 .build());
274             RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
275             if (!rpcResult.isSuccessful()) {
276                 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
277             } else {
278                 return rpcResult.getResult().getInterfaceName();
279             }
280         } catch (InterruptedException | ExecutionException e) {
281             LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstDpId, e);
282         }
283         return null;
284     }
285
286     protected String getTunnelInterfaceName(BigInteger srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
287         .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp) {
288         Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase());
289         Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
290         try {
291             result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
292                 .setSourceDpid(srcDpId)
293                 .setDestinationIp(dstIp)
294                 .setTunnelType(tunType)
295                 .build());
296             RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
297             if (!rpcResult.isSuccessful()) {
298                 LOG.warn("RPC Call to getTunnelInterfaceName returned with Errors {}", rpcResult.getErrors());
299             } else {
300                 return rpcResult.getResult().getInterfaceName();
301             }
302         } catch (InterruptedException | ExecutionException e) {
303             LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstIp, e);
304         }
305         return null;
306     }
307
308     public long createLocalNextHop(long vpnId, BigInteger dpnId,
309                                    String ifName, String ipNextHopAddress, String ipPrefixAddress) {
310         String macAddress = FibUtil.getMacAddressFromPrefix(dataBroker, ifName, ipPrefixAddress);
311         String ipAddress = (macAddress != null) ? ipPrefixAddress : ipNextHopAddress;
312
313         long groupId = createNextHopPointer(getNextHopKey(vpnId, ipAddress));
314         if (groupId == 0) {
315             LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipAddress);
316             return groupId;
317         }
318         String nextHopLockStr = vpnId + ipAddress;
319         synchronized (nextHopLockStr.intern()) {
320             VpnNexthop nexthop = getVpnNexthop(vpnId, ipAddress);
321             LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop,
322                 vpnId, ipAddress, ifName, dpnId);
323             if (nexthop == null) {
324                 if (macAddress == null) {
325                     macAddress = FibUtil.getMacAddressFromPrefix(dataBroker, ifName, ipAddress);
326                 }
327                 List<BucketInfo> listBucketInfo = new ArrayList<>();
328                 List<ActionInfo> listActionInfo = new ArrayList<>();
329                 // MAC re-write
330                 if (macAddress != null) {
331                     int actionKey = listActionInfo.size();
332                     listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey, new MacAddress(macAddress)));
333                     //listActionInfo.add(0, new ActionPopMpls());
334                 } else {
335                     //FIXME: Log message here.
336                     LOG.debug("mac address for new local nexthop is null");
337                 }
338                 listActionInfo.addAll(getEgressActionsForInterface(ifName));
339                 BucketInfo bucket = new BucketInfo(listActionInfo);
340
341                 listBucketInfo.add(bucket);
342                 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
343                     dpnId, groupId, ipAddress, GroupTypes.GroupAll, listBucketInfo);
344                 LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}",
345                     groupId, macAddress, ifName, ipAddress);
346
347                 // install Group
348                 mdsalApiManager.syncInstallGroup(groupEntity, FIXED_DELAY_IN_MILLISECONDS);
349                 try {
350                     LOG.info("Sleeping for {} to wait for the groups to get programmed.", waitTimeForSyncInstall);
351                     Thread.sleep(waitTimeForSyncInstall);
352                 } catch (InterruptedException error) {
353                     LOG.warn("Error while waiting for group {} to install.", groupId);
354                     LOG.debug("{}", error);
355                 }
356                 //update MD-SAL DS
357                 addVpnNexthopToDS(dpnId, vpnId, ipAddress, groupId);
358
359             } else {
360                 //nexthop exists already; a new flow is going to point to it, increment the flowrefCount by 1
361                 int flowrefCnt = nexthop.getFlowrefCount() + 1;
362                 VpnNexthop nh = new VpnNexthopBuilder().setKey(new VpnNexthopKey(ipAddress))
363                     .setFlowrefCount(flowrefCnt).build();
364                 LOG.trace("Updating vpnnextHop {} for refCount {} to Operational DS", nh, flowrefCnt);
365                 syncWrite(LogicalDatastoreType.OPERATIONAL, getVpnNextHopIdentifier(vpnId, ipAddress),
366                     nh, DEFAULT_CALLBACK);
367             }
368         }
369         return groupId;
370     }
371
372     protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String ipPrefix, long egressPointer) {
373         InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
374             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
375
376         // Add nexthop to vpn node
377         VpnNexthop nh = new VpnNexthopBuilder()
378             .setKey(new VpnNexthopKey(ipPrefix))
379             .setDpnId(dpnId)
380             .setIpAddress(ipPrefix)
381             .setFlowrefCount(1)
382             .setEgressPointer(egressPointer).build();
383
384         InstanceIdentifier<VpnNexthop> id1 = idBuilder
385             .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix)).build();
386         LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
387         syncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
388
389     }
390
391     protected InstanceIdentifier<VpnNexthop> getVpnNextHopIdentifier(long vpnId, String ipAddress) {
392         InstanceIdentifier<VpnNexthop> id = InstanceIdentifier.builder(L3nexthop.class)
393             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId)).child(VpnNexthop.class,
394                 new VpnNexthopKey(ipAddress)).build();
395         return id;
396     }
397
398     protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
399
400         // check if vpn node is there
401         InstanceIdentifierBuilder<VpnNexthops> idBuilder =
402             InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
403                 new VpnNexthopsKey(vpnId));
404         InstanceIdentifier<VpnNexthops> id = idBuilder.build();
405         Optional<VpnNexthops> vpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
406         if (vpnNexthops.isPresent()) {
407             // get nexthops list for vpn
408             List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
409             for (VpnNexthop nexthop : nexthops) {
410                 if (nexthop.getIpAddress().equals(ipAddress)) {
411                     // return nexthop
412                     LOG.trace("VpnNextHop : {}", nexthop);
413                     return nexthop;
414                 }
415             }
416             // return null if not found
417         }
418         return null;
419     }
420
421     public AdjacencyResult getRemoteNextHopPointer(BigInteger remoteDpnId, long vpnId, String prefixIp,
422                                                    String nextHopIp) {
423         String egressIfName = null;
424         LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
425             vpnId, prefixIp, nextHopIp);
426
427         Class<? extends InterfaceType> egressIfType;
428         ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
429         if (elanInstance != null) {
430             egressIfType = getInterfaceType(elanInstance);
431         } else {
432             LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
433             egressIfType = Tunnel.class;
434         }
435
436         if (Tunnel.class.equals(egressIfType)) {
437             egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp);
438         } else {
439             egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
440         }
441
442         LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
443             egressIfName);
444         return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType) : null;
445     }
446
447     public BigInteger getDpnForPrefix(long vpnId, String prefixIp) {
448         VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
449         BigInteger localDpnId = (vpnNexthop == null) ? null : vpnNexthop.getDpnId();
450         return localDpnId;
451     }
452
453     private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
454
455         InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
456             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
457             .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
458         InstanceIdentifier<VpnNexthop> id = idBuilder.build();
459         // remove from DS
460         LOG.trace("Removing vpn next hop from datastore : {}", id);
461         syncDelete(LogicalDatastoreType.OPERATIONAL, id);
462     }
463
464     public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String ipNextHopAddress, String ipPrefixAddress) {
465         String ipPrefixStr = vpnId + ipPrefixAddress;
466         VpnNexthop prefixNh = null;
467         synchronized (ipPrefixStr.intern()) {
468             prefixNh = getVpnNexthop(vpnId, ipPrefixAddress);
469         }
470         String ipAddress = (prefixNh != null) ? ipPrefixAddress : ipNextHopAddress;
471
472         String nextHopLockStr = vpnId + ipAddress;
473         synchronized (nextHopLockStr.intern()) {
474             VpnNexthop nh = getVpnNexthop(vpnId, ipAddress);
475             if (nh != null) {
476                 int newFlowrefCnt = nh.getFlowrefCount() - 1;
477                 if (newFlowrefCnt == 0) { //remove the group only if there are no more flows using this group
478                     GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
479                         dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupAll, null);
480                     // remove Group ...
481                     mdsalApiManager.removeGroup(groupEntity);
482                     //update MD-SAL DS
483                     removeVpnNexthopFromDS(vpnId, ipAddress);
484                     //release groupId
485                     removeNextHopPointer(getNextHopKey(vpnId, ipAddress));
486                     LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
487                         nh.getEgressPointer(), vpnId, ipAddress, dpnId);
488                 } else {
489                     //just update the flowrefCount of the vpnNexthop
490                     VpnNexthop currNh = new VpnNexthopBuilder().setKey(new VpnNexthopKey(ipAddress))
491                         .setFlowrefCount(newFlowrefCnt).build();
492                     LOG.trace("Updating vpnnextHop {} for refCount {} to Operational DS", currNh, newFlowrefCnt);
493                     syncWrite(LogicalDatastoreType.OPERATIONAL, getVpnNextHopIdentifier(vpnId, ipAddress), currNh,
494                         DEFAULT_CALLBACK);
495                 }
496             } else {
497                 //throw error
498                 LOG.error("Local Next hop for {} on dpn {} not deleted", ipAddress, dpnId);
499             }
500         }
501     }
502
503
504     // TODO Clean up the exception handling
505     @SuppressWarnings("checkstyle:IllegalCatch")
506     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
507                                                     InstanceIdentifier<T> path) {
508
509         ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction();
510
511         Optional<T> result = Optional.absent();
512         try {
513             result = tx.read(datastoreType, path).get();
514         } catch (Exception e) {
515             throw new RuntimeException(e);
516         }
517
518         return result;
519     }
520
521     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
522                                                    InstanceIdentifier<T> path, T data,
523                                                    FutureCallback<Void> callback) {
524         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
525         tx.merge(datastoreType, path, data, true);
526         Futures.addCallback(tx.submit(), callback);
527     }
528
529     private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
530                                                   InstanceIdentifier<T> path, T data,
531                                                   FutureCallback<Void> callback) {
532         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
533         tx.merge(datastoreType, path, data, true);
534         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
535         try {
536             futures.get();
537         } catch (InterruptedException | ExecutionException e) {
538             LOG.error("Error writing to datastore (path, data) : ({}, {})", path, data, e);
539             throw new RuntimeException(e.getMessage());
540         }
541     }
542
543     private <T extends DataObject> void syncDelete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
544         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
545         tx.delete(datastoreType, path);
546         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
547         try {
548             futures.get();
549         } catch (InterruptedException | ExecutionException e) {
550             LOG.error("Error deleting from datastore (path) : ({})", path, e);
551             throw new RuntimeException(e.getMessage());
552         }
553     }
554
555     private InstanceIdentifier<Adjacency> getAdjacencyIdentifier(String vpnInterfaceName, String ipAddress) {
556         return InstanceIdentifier.builder(VpnInterfaces.class)
557             .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).augmentation(
558                 Adjacencies.class).child(Adjacency.class, new AdjacencyKey(ipAddress)).build();
559     }
560
561     InstanceIdentifier<Adjacencies> getAdjListPath(String vpnInterfaceName) {
562         return InstanceIdentifier.builder(VpnInterfaces.class)
563             .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).augmentation(
564                 Adjacencies.class).build();
565     }
566
567     // TODO Clean up the console output
568     @SuppressWarnings("checkstyle:RegexpSinglelineJava")
569     public void setConfTransType(String service, String transportType) {
570
571         if (!service.toUpperCase().equals("L3VPN")) {
572             System.out.println("Please provide a valid service name. Available value(s): L3VPN");
573             LOG.error("Incorrect service {} provided for setting the transport type.", service);
574             return;
575         }
576
577         L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType.toUpperCase());
578
579         if (transType != L3VPNTransportTypes.Invalid) {
580             configuredTransportTypeL3VPN = transType;
581         }
582     }
583
584     public void writeConfTransTypeConfigDS() {
585         FibUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
586             createConfTransportType(configuredTransportTypeL3VPN.getTransportType()),
587             FibUtil.DEFAULT_CALLBACK);
588     }
589
590     public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
591         return this.configuredTransportTypeL3VPN;
592     }
593
594     public String getReqTransType() {
595         if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
596             /*
597             * Restart scenario, Read from the ConfigDS.
598             * if the value is Unset, cache value as VxLAN.
599             */
600             LOG.trace("configureTransportType is not yet set.");
601             Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
602                 FibUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
603
604             if (configuredTransTypeFromConfig.isPresent()) {
605                 if (configuredTransTypeFromConfig.get().getTransportType().equals(TunnelTypeGre.class)) {
606                     configuredTransportTypeL3VPN.setL3VPNTransportTypes(ITMConstants.TUNNEL_TYPE_GRE);
607                 } else {
608                     configuredTransportTypeL3VPN.setL3VPNTransportTypes(ITMConstants.TUNNEL_TYPE_VXLAN);
609                 }
610                 LOG.trace("configuredTransportType set from config DS to {}",
611                     getConfiguredTransportTypeL3VPN().getTransportType());
612             } else {
613                 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
614                 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
615             }
616         } else {
617             LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
618         }
619         return getConfiguredTransportTypeL3VPN().getTransportType();
620     }
621
622     public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
623         return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
624     }
625
626     private ConfTransportTypeL3vpn createConfTransportType(String type) {
627         ConfTransportTypeL3vpn confTransType;
628         switch (type) {
629             case ITMConstants.TUNNEL_TYPE_GRE:
630                 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
631                 LOG.trace("Setting the confTransportType to GRE.");
632                 break;
633             case ITMConstants.TUNNEL_TYPE_VXLAN:
634                 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
635                 LOG.trace("Setting the confTransportType to VxLAN.");
636                 break;
637             default:
638                 LOG.trace("Invalid transport type {} passed to Config DS ", type);
639                 confTransType = null;
640                 break;
641         }
642         return confTransType;
643     }
644
645     public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
646         switch (transportType) {
647             case "VXLAN":
648                 return TunnelTypeVxlan.class;
649             case "GRE":
650                 return TunnelTypeGre.class;
651             default:
652                 return TunnelTypeMplsOverGre.class;
653         }
654     }
655
656     public String getTransportTypeStr(String tunType) {
657         if (tunType.equals(TunnelTypeVxlan.class.toString())) {
658             return ITMConstants.TUNNEL_TYPE_VXLAN;
659         } else if (tunType.equals(TunnelTypeGre.class.toString())) {
660             return ITMConstants.TUNNEL_TYPE_GRE;
661         } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
662             return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
663         } else {
664             return ITMConstants.TUNNEL_TYPE_INVALID;
665         }
666     }
667
668     @Override
669     public void close() throws Exception {
670         LOG.info("{} close", getClass().getSimpleName());
671     }
672
673     // TODO Clean up the exception handling
674     @SuppressWarnings("checkstyle:IllegalCatch")
675     private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp) {
676         if (nextHopIp != null && !nextHopIp.isEmpty()) {
677             try {
678                 // here use the config for tunnel type param
679                 return getTunnelInterfaceName(remoteDpnId,
680                     org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
681                         .getDefaultInstance(nextHopIp));
682             } catch (Exception ex) {
683                 LOG.error("Error while retrieving nexthop pointer for nexthop {} : ", nextHopIp, ex);
684             }
685         }
686
687         return null;
688     }
689
690     private String getExtPortRemoteNextHopPointer(BigInteger remoteDpnId, ElanInstance elanInstance) {
691         return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
692     }
693
694     /**
695      * Get the interface type associated with the type of ELAN used for routing
696      * traffic to/from remote compute nodes.
697      *
698      * @param elanInstance The elan instance
699      * @return L2vlan for flat/VLAN network type and Tunnel otherwise
700      */
701     private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
702         Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
703         if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
704             return L2vlan.class;
705         }
706
707         return Tunnel.class;
708     }
709
710     private ElanInstance getElanInstanceForPrefix(long vpnId, String prefixIp) {
711         Prefixes prefix = FibUtil.getPrefixToInterface(dataBroker, vpnId, prefixIp);
712         if (prefix == null) {
713             LOG.warn("No prefix info was found for VPN id {} prefix {}", vpnId, prefixIp);
714             return null;
715         }
716
717         String interfaceName = prefix.getVpnInterfaceName();
718         if (interfaceName == null) {
719             LOG.warn("No VPN interface found for VPN id {} prefix {}", vpnId, prefixIp);
720             return null;
721         }
722
723         ElanInterface elanInterface = elanService.getElanInterfaceByElanInterfaceName(interfaceName);
724         if (elanInterface == null) {
725             LOG.warn("No ELAN interface found for VPN interface {} on VPN id {}", interfaceName, vpnId);
726             return null;
727         }
728
729         return elanService.getElanInstance(elanInterface.getElanInstanceName());
730     }
731
732     static class AdjacencyResult {
733         private String interfaceName;
734         private Class<? extends InterfaceType> interfaceType;
735
736         AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType) {
737             this.interfaceName = interfaceName;
738             this.interfaceType = interfaceType;
739         }
740
741         public String getInterfaceName() {
742             return interfaceName;
743         }
744
745         public Class<? extends InterfaceType> getInterfaceType() {
746             return interfaceType;
747         }
748
749         @Override
750         public int hashCode() {
751             final int prime = 31;
752             int result = 1;
753             result = prime * result + ((interfaceName == null) ? 0 : interfaceName.hashCode());
754             return result;
755         }
756
757         @Override
758         public boolean equals(Object obj) {
759             boolean result = false;
760             if (getClass() != obj.getClass()) {
761                 return result;
762             } else {
763                 AdjacencyResult other = (AdjacencyResult) obj;
764                 result = interfaceName.equals(other.interfaceName);
765             }
766             return result;
767         }
768     }
769 }