Merge "Supporting DHCP as a Service"
[netvirt.git] / vpnservice / elanmanager / elanmanager-impl / src / main / java / org / opendaylight / netvirt / elan / internal / ElanInterfaceManager.java
1 /*
2  * Copyright (c) 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.elan.internal;
9
10 import java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Queue;
16 import java.util.Set;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ConcurrentLinkedQueue;
19 import java.util.concurrent.ConcurrentMap;
20
21 import org.apache.commons.lang3.StringUtils;
22 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
25 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
26 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
27 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
28 import org.opendaylight.netvirt.elan.utils.ElanConstants;
29 import org.opendaylight.netvirt.elan.utils.ElanUtils;
30 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
31 import org.opendaylight.genius.itm.globals.ITMConstants;
32 import org.opendaylight.genius.mdsalutil.ActionInfo;
33 import org.opendaylight.genius.mdsalutil.ActionType;
34 import org.opendaylight.genius.mdsalutil.FlowEntity;
35 import org.opendaylight.genius.mdsalutil.InstructionInfo;
36 import org.opendaylight.genius.mdsalutil.InstructionType;
37 import org.opendaylight.genius.mdsalutil.MDSALUtil;
38 import org.opendaylight.genius.mdsalutil.MatchFieldType;
39 import org.opendaylight.genius.mdsalutil.MatchInfo;
40 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
41 import org.opendaylight.genius.mdsalutil.NwConstants;
42 //import org.opendaylight.genius.neutronvpn.api.l2gw.L2GatewayDevice;
43 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnel;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanForwardingTables;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMacBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMacKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfacesBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfacesKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.forwarding.tables.MacTable;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.forwarding.tables.MacTableKey;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.state.Elan;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.state.ElanBuilder;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.state.ElanKey;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntry;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntryBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntryKey;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnel;
78 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
79 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
80 import org.slf4j.Logger;
81 import org.slf4j.LoggerFactory;
82
83 import com.google.common.base.Optional;
84 import com.google.common.base.Preconditions;
85
86 /**
87  * Class in charge of handling creations, modifications and removals of ElanInterfaces.
88  *
89  * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface
90  *
91  */
92 @SuppressWarnings( "deprecation" )
93 public class ElanInterfaceManager extends AsyncDataTreeChangeListenerBase<ElanInterface,ElanInterfaceManager> implements AutoCloseable {
94
95     private ElanServiceProvider elanServiceProvider = null;
96     private static volatile ElanInterfaceManager elanInterfaceManager = null;
97     private static long waitTimeForSyncInstall;
98
99     private Map<String, ConcurrentLinkedQueue<ElanInterface>> unProcessedElanInterfaces =
100         new ConcurrentHashMap<String, ConcurrentLinkedQueue<ElanInterface>> ();
101
102     private static final Logger logger = LoggerFactory.getLogger(ElanInterfaceManager.class);
103
104     public ElanInterfaceManager(ElanServiceProvider elanServiceProvider) {
105         super(ElanInterface.class,ElanInterfaceManager.class);
106         this.elanServiceProvider = elanServiceProvider;
107     }
108
109     public static ElanInterfaceManager getElanInterfaceManager(ElanServiceProvider elanServiceProvider) {
110         if (elanInterfaceManager == null) {
111             synchronized (ElanInterfaceManager.class) {
112                 if (elanInterfaceManager == null) {
113                     elanInterfaceManager = new ElanInterfaceManager(elanServiceProvider);
114                     Long waitTime = Long.getLong("wait.time.sync.install");
115                     if (waitTime == null) {
116                         waitTime = 300L;
117                     }
118                     waitTimeForSyncInstall = waitTime;
119                 }
120             }
121         }
122         return elanInterfaceManager;
123     }
124
125     protected InstanceIdentifier<ElanInterface> getWildCardPath() {
126         return InstanceIdentifier.create(ElanInterfaces.class).child(ElanInterface.class);
127     }
128
129     @Override
130     protected void remove(InstanceIdentifier<ElanInterface> identifier, ElanInterface del) {
131         String interfaceName =  del.getName();
132         ElanInstance elanInfo = ElanUtils.getElanInstanceByName(del.getElanInstanceName());
133         /*
134          * Handling in case the elan instance is deleted.If the Elan instance is deleted, there is no need to explicitly delete the elan interfaces
135          */
136         if (elanInfo == null) {
137             return;
138         }
139         InterfaceInfo interfaceInfo = elanServiceProvider.getInterfaceManager().getInterfaceInfo(interfaceName);
140         String elanInstanceName = elanInfo.getElanInstanceName();
141         DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance();
142         InterfaceRemoveWorkerOnElan configWorker = new InterfaceRemoveWorkerOnElan(elanInstanceName, elanInfo, interfaceName, interfaceInfo, false, this);
143         coordinator.enqueueJob(elanInstanceName, configWorker, ElanConstants.JOB_MAX_RETRIES);
144     }
145
146     public void removeElanInterface(ElanInstance elanInfo, String interfaceName, InterfaceInfo interfaceInfo, boolean isInterfaceStateRemoved) {
147         String elanName = elanInfo.getElanInstanceName();
148         boolean isLastElanInterface = false;
149         long elanTag = elanInfo.getElanTag();
150         WriteTransaction tx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
151         WriteTransaction deleteFlowGroupTx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
152         Elan elanState = removeElanStateForInterface(elanInfo, interfaceName, tx);
153         if (elanState == null) {
154             return;
155         }
156         List<String> elanInterfaces = elanState.getElanInterfaces();
157         if (elanInterfaces.size() == 0) {
158             isLastElanInterface = true;
159         }
160         if (interfaceInfo != null) {
161             BigInteger dpId = interfaceInfo.getDpId();
162             DpnInterfaces dpnInterfaces = removeElanDpnInterfaceFromOperationalDataStore(elanName, dpId, interfaceName, elanTag, tx);
163             /*
164              * If there are not elan ports, remove the unknown dmac, terminating service table
165              * flows, remote/local bc group
166              */
167             if (dpnInterfaces == null || dpnInterfaces.getInterfaces() == null || dpnInterfaces.getInterfaces().isEmpty()) {
168                 // No more Elan Interfaces in this DPN
169                 logger.debug("deleting the elan: {} present on dpId: {}", elanInfo.getElanInstanceName(), dpId);
170                 removeDefaultTermFlow(dpId, elanInfo.getElanTag());
171                 removeUnknownDmacFlow(dpId, elanInfo, deleteFlowGroupTx);
172                 removeElanBroadcastGroup(elanInfo, interfaceInfo, deleteFlowGroupTx);
173                 removeLocalBroadcastGroup(elanInfo, interfaceInfo, deleteFlowGroupTx);
174                 if ( elanInfo.getVni() != null && elanInfo.getVni().longValue() != 0 ) {
175                     unsetExternalTunnelTable(dpId, elanInfo);
176                 }
177             } else {
178                 setupLocalBroadcastGroups(elanInfo, dpnInterfaces, interfaceInfo);
179             }
180         }
181         ElanUtils.waitForTransactionToComplete(tx);
182         deleteFlowGroupTx.submit();
183         DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance();
184         InterfaceRemoveWorkerOnElanInterface removeInterfaceWorker = new InterfaceRemoveWorkerOnElanInterface(interfaceName, elanInfo,
185             interfaceInfo, isInterfaceStateRemoved, this, isLastElanInterface);
186         coordinator.enqueueJob(interfaceName, removeInterfaceWorker, ElanConstants.JOB_MAX_RETRIES);
187     }
188
189     private Elan removeElanStateForInterface(ElanInstance elanInfo, String interfaceName, WriteTransaction tx) {
190         String elanName = elanInfo.getElanInstanceName();
191         Elan elanState = ElanUtils.getElanByName(elanName);
192         if(elanState == null) {
193             return elanState;
194         }
195         List<String> elanInterfaces = elanState.getElanInterfaces();
196         elanInterfaces.remove(interfaceName);
197         if(elanInterfaces.isEmpty()) {
198             tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanName));
199             tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanMacTableOperationalDataPath(elanName));
200             tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInfoEntriesOperationalDataPath(elanInfo.getElanTag()));
201         } else {
202             Elan updateElanState = new ElanBuilder().setElanInterfaces(elanInterfaces).setName(elanName).setKey(new ElanKey(elanName)).build();
203             tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanName), updateElanState);
204         }
205         return elanState;
206     }
207
208     private void deleteElanInterfaceFromConfigDS(String interfaceName, WriteTransaction tx) {
209         //removing the ElanInterface from the config data_store if interface is not present in Interface config DS
210         if (elanServiceProvider.getInterfaceManager().getInterfaceInfoFromConfigDataStore(interfaceName) == null) {
211             tx.delete(LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName));
212         }
213     }
214
215     void removeEntriesForElanInterface(ElanInstance elanInfo, InterfaceInfo interfaceInfo, String interfaceName, boolean isInterfaceStateRemoved, boolean isLastElanInterface) {
216         String elanName = elanInfo.getElanInstanceName();
217         WriteTransaction tx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
218         WriteTransaction deleteFlowGroupTx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
219         InstanceIdentifier<ElanInterfaceMac> elanInterfaceId = ElanUtils.getElanInterfaceMacEntriesOperationalDataPath(interfaceName);
220         logger.debug("Removing the Interface:{} from elan:{}", interfaceName, elanName);
221         if (interfaceInfo != null) {
222             Optional<ElanInterfaceMac> existingElanInterfaceMac = ElanUtils.read(elanServiceProvider.getBroker(), LogicalDatastoreType.OPERATIONAL, elanInterfaceId);
223             if(existingElanInterfaceMac.isPresent()) {
224                 List<PhysAddress> macAddresses = new ArrayList<PhysAddress>();
225                 List<MacEntry> existingMacEntries = existingElanInterfaceMac.get().getMacEntry();
226                 List<MacEntry> macEntries = new ArrayList<>();
227                 if (existingMacEntries != null && !existingMacEntries.isEmpty()) {
228                     macEntries.addAll(existingMacEntries);
229                 }
230                 if(!macEntries.isEmpty()) {
231                     for (MacEntry macEntry : macEntries) {
232                         logger.debug("removing the  mac-entry:{} present on elanInterface:{}", macEntry.getMacAddress().getValue(), interfaceName);
233                         if (!isLastElanInterface) {
234                             tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getMacEntryOperationalDataPath(elanName, macEntry.getMacAddress()));
235                         }
236                         ElanUtils.deleteMacFlows(elanInfo, interfaceInfo, macEntry, deleteFlowGroupTx);
237                         macAddresses.add(macEntry.getMacAddress());
238                     }
239
240                     // Removing all those MACs from External Devices belonging to this ELAN
241                     if ( elanInfo.getVni() != null && elanInfo.getVni() != 0 ) {
242                         ElanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo, macAddresses);
243                     }
244                 }
245             }
246             removeDefaultTermFlow(interfaceInfo.getDpId(), interfaceInfo.getInterfaceTag());
247             removeFilterEqualsTable(elanInfo, interfaceInfo, deleteFlowGroupTx);
248         } else {
249             // Interface does not exist in ConfigDS, so lets remove everything about that interface related to Elan
250             ElanInterfaceMac elanInterfaceMac =  ElanUtils.getElanInterfaceMacByInterfaceName(interfaceName);
251             if(elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) {
252                 List<MacEntry> macEntries = elanInterfaceMac.getMacEntry();
253                 for(MacEntry macEntry : macEntries) {
254                     tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getMacEntryOperationalDataPath(elanName, macEntry.getMacAddress()));
255                 }
256             }
257         }
258         tx.delete(LogicalDatastoreType.OPERATIONAL, elanInterfaceId);
259         if(!isInterfaceStateRemoved) {
260             unbindService(elanInfo, interfaceName, tx);
261         }
262         deleteElanInterfaceFromConfigDS(interfaceName, tx);
263         ElanUtils.waitForTransactionToComplete(tx);
264         deleteFlowGroupTx.submit();
265     }
266
267     private DpnInterfaces removeElanDpnInterfaceFromOperationalDataStore(String elanName, BigInteger dpId, String interfaceName,
268                                                                          long elanTag, WriteTransaction tx) {
269         DpnInterfaces dpnInterfaces =  ElanUtils.getElanInterfaceInfoByElanDpn(elanName, dpId);
270         if(dpnInterfaces != null) {
271             List<String> interfaceLists = dpnInterfaces.getInterfaces();
272             interfaceLists.remove(interfaceName);
273
274             if (interfaceLists == null || interfaceLists.isEmpty()) {
275                 deleteAllRemoteMacsInADpn(elanName, dpId, elanTag);
276                 deleteElanDpnInterface(elanName, dpId, tx);
277             } else {
278                 dpnInterfaces = updateElanDpnInterfacesList(elanName, dpId, interfaceLists, tx);
279             }
280         }
281         return dpnInterfaces;
282     }
283
284     private void deleteAllRemoteMacsInADpn(String elanName, BigInteger dpId, long elanTag) {
285         List<DpnInterfaces> dpnInterfaces = ElanUtils.getInvolvedDpnsInElan(elanName);
286         for (DpnInterfaces dpnInterface : dpnInterfaces) {
287             BigInteger currentDpId = dpnInterface.getDpId();
288             if (!currentDpId.equals(dpId)) {
289                 for (String elanInterface : dpnInterface.getInterfaces()) {
290                     ElanInterfaceMac macs = ElanUtils.getElanInterfaceMacByInterfaceName(elanInterface);
291                     if (macs == null) {
292                         continue;
293                     }
294                     for (MacEntry mac : macs.getMacEntry())
295                         elanServiceProvider.getMdsalManager().removeFlow(dpId, MDSALUtil.buildFlow(ElanConstants.ELAN_DMAC_TABLE,
296                             ElanUtils.getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpId, currentDpId, mac.getMacAddress().getValue(), elanTag)));
297                 }
298             }
299         }
300     }
301
302     @Override
303     protected void update(InstanceIdentifier<ElanInterface> identifier, ElanInterface original, ElanInterface update) {
304         // updating the static-Mac Entries for the existing elanInterface
305         String elanName = update.getElanInstanceName();
306         String interfaceName = update.getName();
307         InterfaceInfo interfaceInfo = elanServiceProvider.getInterfaceManager().getInterfaceInfo(interfaceName);
308         List<PhysAddress> existingPhysAddress = original.getStaticMacEntries();
309         List<PhysAddress> updatedPhysAddress = update.getStaticMacEntries();
310         if(updatedPhysAddress != null && !updatedPhysAddress.isEmpty()) {
311             List<PhysAddress> existingClonedPhyAddress = new ArrayList<>();
312             if(existingPhysAddress != null && !existingPhysAddress.isEmpty()) {
313                 existingClonedPhyAddress.addAll(0, existingPhysAddress);
314                 existingPhysAddress.removeAll(updatedPhysAddress);
315                 updatedPhysAddress.removeAll(existingClonedPhyAddress);
316                 // removing the PhyAddress which are not presented in the updated List
317                 for(PhysAddress physAddress: existingPhysAddress) {
318                     removeInterfaceStaticMacEntires(elanName, interfaceName, physAddress);
319                 }
320             }
321             // Adding the new PhysAddress which are presented in the updated List
322             if(updatedPhysAddress.size() > 0) {
323                 for(PhysAddress physAddress: updatedPhysAddress) {
324                     InstanceIdentifier<MacEntry> macId =  getMacEntryOperationalDataPath(elanName, physAddress);
325                     Optional<MacEntry> existingMacEntry = ElanUtils.read(elanServiceProvider.getBroker(), LogicalDatastoreType.OPERATIONAL, macId);
326                     WriteTransaction tx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
327                     if(existingMacEntry.isPresent()) {
328                         elanServiceProvider.getElanForwardingEntriesHandler().updateElanInterfaceForwardingTablesList(elanName, interfaceName, existingMacEntry.get().getInterface(), existingMacEntry.get(), tx);
329                     } else {
330                         elanServiceProvider.getElanForwardingEntriesHandler().addElanInterfaceForwardingTableList(ElanUtils.getElanInstanceByName(elanName), interfaceName, physAddress, tx);
331                     }
332                     ElanUtils.waitForTransactionToComplete(tx);
333                 }
334             }
335         } else if(existingPhysAddress != null && !existingPhysAddress.isEmpty()) {
336             for( PhysAddress physAddress : existingPhysAddress) {
337                 removeInterfaceStaticMacEntires(elanName, interfaceName, physAddress);
338             }
339         }
340     }
341
342     @Override
343     protected void add(InstanceIdentifier<ElanInterface> identifier, ElanInterface elanInterfaceAdded) {
344         String elanInstanceName = elanInterfaceAdded.getElanInstanceName();
345         String interfaceName = elanInterfaceAdded.getName();
346         InterfaceInfo interfaceInfo = elanServiceProvider.getInterfaceManager().getInterfaceInfo(interfaceName);
347         if (interfaceInfo == null) {
348             logger.warn("Interface {} is removed from Interface Oper DS due to port down ", interfaceName);
349             return;
350         }
351         ElanInstance elanInstance = ElanUtils.getElanInstanceByName(elanInstanceName);
352
353         if (elanInstance == null) {
354             elanInstance = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName).setDescription(elanInterfaceAdded.getDescription()).build();
355             //Add the ElanInstance in the Configuration data-store
356             WriteTransaction tx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
357             List<String> elanInterfaces = new ArrayList<String>();
358             elanInterfaces.add(interfaceName);
359             ElanUtils.updateOperationalDataStore(elanServiceProvider.getBroker(), elanServiceProvider.getIdManager(), elanInstance, elanInterfaces, tx);
360             ElanUtils.waitForTransactionToComplete(tx);
361             elanInstance = ElanUtils.getElanInstanceByName(elanInstanceName);
362         }
363
364
365         Long elanTag = elanInstance.getElanTag();
366         // If elan tag is not updated, then put the elan interface into unprocessed entry map and entry. Let entries
367         // in this map get processed during ELAN update DCN.
368         if (elanTag == null) {
369             ConcurrentLinkedQueue<ElanInterface> elanInterfaces = unProcessedElanInterfaces.get(elanInstanceName);
370             if (elanInterfaces == null) {
371                 elanInterfaces = new ConcurrentLinkedQueue<ElanInterface>();
372             }
373             elanInterfaces.add(elanInterfaceAdded);
374             unProcessedElanInterfaces.put(elanInstanceName, elanInterfaces);
375             return;
376         }
377         DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance();
378         InterfaceAddWorkerOnElan addWorker = new InterfaceAddWorkerOnElan(elanInstanceName, elanInterfaceAdded,
379             interfaceInfo, elanInstance, this);
380         coordinator.enqueueJob(elanInstanceName, addWorker, ElanConstants.JOB_MAX_RETRIES);
381     }
382
383     void handleunprocessedElanInterfaces(ElanInstance elanInstance) {
384         Queue<ElanInterface> elanInterfaces = unProcessedElanInterfaces.get(elanInstance.getElanInstanceName());
385         if (elanInterfaces == null || elanInterfaces.isEmpty()) {
386             return;
387         }
388         for (ElanInterface elanInterface: elanInterfaces) {
389             String interfaceName = elanInterface.getName();
390             InterfaceInfo interfaceInfo = elanServiceProvider.getInterfaceManager().getInterfaceInfo(interfaceName);
391             addElanInterface(elanInterface, interfaceInfo, elanInstance);
392         }
393     }
394
395     void programRemoteDmacFlow(ElanInstance elanInstance, InterfaceInfo interfaceInfo, WriteTransaction writeFlowGroupTx){
396         ElanDpnInterfacesList elanDpnInterfacesList =  ElanUtils.getElanDpnInterfacesList(elanInstance.getElanInstanceName());
397         List<DpnInterfaces> dpnInterfaceLists =  null;
398         if (elanDpnInterfacesList != null) {
399             dpnInterfaceLists = elanDpnInterfacesList.getDpnInterfaces();
400         }
401         if (dpnInterfaceLists == null) {
402             dpnInterfaceLists = new ArrayList<DpnInterfaces>();
403         }
404         for(DpnInterfaces dpnInterfaces : dpnInterfaceLists){
405             if(dpnInterfaces.getDpId().equals(interfaceInfo.getDpId())) {
406                 continue;
407             }
408             List<String> remoteElanInterfaces = dpnInterfaces.getInterfaces();
409             for(String remoteIf : remoteElanInterfaces) {
410                 ElanInterfaceMac elanIfMac = ElanUtils.getElanInterfaceMacByInterfaceName(remoteIf);
411                 InterfaceInfo remoteInterface = elanServiceProvider.getInterfaceManager().getInterfaceInfo(remoteIf);
412                 if(elanIfMac == null) {
413                     continue;
414                 }
415                 List<MacEntry> remoteMacEntries = elanIfMac.getMacEntry();
416                 if(remoteMacEntries != null) {
417                     for (MacEntry macEntry : remoteMacEntries) {
418                         PhysAddress physAddress = macEntry.getMacAddress();
419                         ElanUtils.setupRemoteDmacFlow(interfaceInfo.getDpId(), remoteInterface.getDpId(),
420                             remoteInterface.getInterfaceTag(),
421                             elanInstance.getElanTag(),
422                             physAddress.getValue(),
423                             elanInstance.getElanInstanceName(), writeFlowGroupTx);
424                     }
425                 }
426             }
427         }
428     }
429
430     void addElanInterface(ElanInterface elanInterface, InterfaceInfo interfaceInfo, ElanInstance elanInstance) {
431         Preconditions.checkNotNull(elanInstance, "elanInstance cannot be null");
432         Preconditions.checkNotNull(interfaceInfo, "interfaceInfo cannot be null");
433         Preconditions.checkNotNull(elanInterface, "elanInterface cannot be null");
434
435         String interfaceName = elanInterface.getName();
436         String elanInstanceName = elanInterface.getElanInstanceName();
437
438         Elan elanInfo = ElanUtils.getElanByName(elanInstanceName);
439         WriteTransaction tx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
440         if (elanInfo == null) {
441             List<String> elanInterfaces = new ArrayList<String>();
442             elanInterfaces.add(interfaceName);
443             ElanUtils.updateOperationalDataStore(elanServiceProvider.getBroker(), elanServiceProvider.getIdManager(), elanInstance, elanInterfaces, tx);
444         } else {
445             createElanStateList(elanInstanceName, interfaceName, tx);
446         }
447         boolean isFirstInterfaceInDpn = false;
448         // Specific actions to the DPN where the ElanInterface has been added, for example, programming the
449         // External tunnel table if needed or adding the ElanInterface to the DpnInterfaces in the operational DS.
450         BigInteger dpId = ( interfaceInfo != null ) ? dpId = interfaceInfo.getDpId() : null;
451         DpnInterfaces dpnInterfaces = null;
452         if (dpId != null && !dpId.equals(ElanConstants.INVALID_DPN)) {
453             InstanceIdentifier<DpnInterfaces> elanDpnInterfaces = ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId);
454             Optional<DpnInterfaces> existingElanDpnInterfaces = ElanUtils.read(elanServiceProvider.getBroker(), LogicalDatastoreType.OPERATIONAL, elanDpnInterfaces);
455             if (!existingElanDpnInterfaces.isPresent()) {
456                 isFirstInterfaceInDpn = true;
457                 // ELAN's 1st ElanInterface added to this DPN
458                 dpnInterfaces = createElanInterfacesList(elanInstanceName, interfaceName, dpId, tx);
459                 // The 1st ElanInterface in a DPN must program the Ext Tunnel table, but only if Elan has VNI
460                 if ( elanInstance.getVni() != null && elanInstance.getVni().longValue() != 0 ) {
461                     setExternalTunnelTable(dpId, elanInstance);
462                 }
463                 ElanL2GatewayUtils.installElanL2gwDevicesLocalMacsInDpn(dpId, elanInstance);
464             } else {
465                 List<String> elanInterfaces = existingElanDpnInterfaces.get().getInterfaces();
466                 elanInterfaces.add(interfaceName);
467                 if (elanInterfaces.size() == 1) {//1st dpn interface
468                     ElanL2GatewayUtils.installElanL2gwDevicesLocalMacsInDpn(dpId, elanInstance);
469                 }
470                 dpnInterfaces = updateElanDpnInterfacesList(elanInstanceName, dpId, elanInterfaces, tx);
471             }
472         }
473
474         // add code to install Local/Remote BC group, unknow DMAC entry, terminating service table flow entry
475         // call bindservice of interfacemanager to create ingress table flow enty.
476         //Add interface to the ElanInterfaceForwardingEntires Container
477         createElanInterfaceTablesList(interfaceName, tx);
478         if (interfaceInfo != null) {
479             installEntriesForFirstInterfaceonDpn(elanInstance, interfaceInfo, dpnInterfaces, isFirstInterfaceInDpn, tx);
480         }
481         ElanUtils.waitForTransactionToComplete(tx);
482
483         DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance();
484         InterfaceAddWorkerOnElanInterface addWorker = new InterfaceAddWorkerOnElanInterface(interfaceName, elanInterface,
485             interfaceInfo, elanInstance, isFirstInterfaceInDpn, this);
486         coordinator.enqueueJob(interfaceName, addWorker, ElanConstants.JOB_MAX_RETRIES);
487     }
488
489     void setupEntriesForElanInterface(ElanInstance elanInstance, ElanInterface elanInterface,
490                                       InterfaceInfo interfaceInfo, boolean isFirstInterfaceInDpn) {
491         String elanInstanceName = elanInstance.getElanInstanceName();
492         String interfaceName = elanInterface.getName();
493         WriteTransaction tx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
494         BigInteger dpId = interfaceInfo.getDpId();
495         WriteTransaction writeFlowGroupTx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
496         installEntriesForElanInterface(elanInstance, interfaceInfo, isFirstInterfaceInDpn, tx, writeFlowGroupTx);
497         List<PhysAddress> staticMacAddresses = elanInterface.getStaticMacEntries();
498         if (staticMacAddresses != null) {
499             boolean isInterfaceOperational = isOperational(interfaceInfo);
500             for (PhysAddress physAddress : staticMacAddresses) {
501                 InstanceIdentifier<MacEntry> macId = getMacEntryOperationalDataPath(elanInstanceName, physAddress);
502                 Optional<MacEntry> existingMacEntry = ElanUtils.read(elanServiceProvider.getBroker(), LogicalDatastoreType.OPERATIONAL, macId);
503                 if (existingMacEntry.isPresent()) {
504                     elanServiceProvider.getElanForwardingEntriesHandler().updateElanInterfaceForwardingTablesList(elanInstanceName, interfaceName, existingMacEntry.get().getInterface(),
505                         existingMacEntry.get(), tx);
506                 } else {
507                     elanServiceProvider.getElanForwardingEntriesHandler().addElanInterfaceForwardingTableList(elanInstance, interfaceName, physAddress, tx);
508                 }
509
510                 if ( isInterfaceOperational ) {
511                     // Setting SMAC, DMAC, UDMAC in this DPN and also in other DPNs
512                     ElanUtils.setupMacFlows(elanInstance, interfaceInfo, ElanConstants.STATIC_MAC_TIMEOUT, physAddress.getValue(), writeFlowGroupTx);
513                 }
514             }
515
516             if ( isInterfaceOperational ) {
517                 // Add MAC in TOR's remote MACs via OVSDB. Outside of the loop on purpose.
518                 ElanL2GatewayUtils.scheduleAddDpnMacInExtDevices(elanInstance.getElanInstanceName(), dpId, staticMacAddresses);
519             }
520         }
521         ElanUtils.waitForTransactionToComplete(tx);
522         writeFlowGroupTx.submit();
523     }
524
525     protected void removeInterfaceStaticMacEntires(String elanInstanceName, String interfaceName, PhysAddress physAddress) {
526         InterfaceInfo interfaceInfo = elanServiceProvider.getInterfaceManager().getInterfaceInfo(interfaceName);
527         InstanceIdentifier<MacEntry> macId =  getMacEntryOperationalDataPath(elanInstanceName, physAddress);
528         Optional<MacEntry> existingMacEntry = ElanUtils.read(elanServiceProvider.getBroker(), LogicalDatastoreType.OPERATIONAL, macId);
529
530         if (!existingMacEntry.isPresent()) {
531             return;
532         }
533
534         MacEntry macEntry = new MacEntryBuilder().setMacAddress(physAddress).setInterface(interfaceName).setKey(new MacEntryKey(physAddress)).build();
535         WriteTransaction tx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
536         elanServiceProvider.getElanForwardingEntriesHandler().deleteElanInterfaceForwardingEntries(ElanUtils.getElanInstanceByName(elanInstanceName), interfaceInfo, macEntry, tx);
537         elanServiceProvider.getElanForwardingEntriesHandler().deleteElanInterfaceMacForwardingEntries(interfaceName, physAddress, tx);
538         ElanUtils.waitForTransactionToComplete(tx);
539     }
540
541
542     private InstanceIdentifier<MacEntry> getMacEntryOperationalDataPath(String elanName, PhysAddress physAddress) {
543         return InstanceIdentifier.builder(ElanForwardingTables.class).child(MacTable.class,
544             new MacTableKey(elanName)).child(MacEntry.class, new MacEntryKey(physAddress)).build();
545     }
546
547     private void installEntriesForElanInterface(ElanInstance elanInstance, InterfaceInfo interfaceInfo,
548                                                 boolean isFirstInterfaceInDpn, WriteTransaction tx, WriteTransaction writeFlowGroupTx) {
549         if (!isOperational(interfaceInfo)) {
550             return;
551         }
552         BigInteger dpId = interfaceInfo.getDpId();
553         ElanUtils.setupTermDmacFlows(interfaceInfo, elanServiceProvider.getMdsalManager(), writeFlowGroupTx);
554         setupFilterEqualsTable(elanInstance, interfaceInfo, writeFlowGroupTx);
555         if (isFirstInterfaceInDpn) {
556             //Terminating Service , UnknownDMAC Table.
557             setupTerminateServiceTable(elanInstance, dpId, writeFlowGroupTx);
558             setupUnknownDMacTable(elanInstance, dpId, writeFlowGroupTx);
559             //update the remote-DPNs remoteBC group entry with Tunnels
560             setElanBCGrouponOtherDpns(elanInstance, dpId, writeFlowGroupTx);
561             /*
562              * Install remote DMAC flow.
563              * This is required since this DPN is added later to the elan instance
564              * and remote DMACs of other interfaces in this elan instance are not present in the current dpn.
565              */
566             programRemoteDmacFlow(elanInstance, interfaceInfo, writeFlowGroupTx);
567         }
568         // bind the Elan service to the Interface
569         bindService(elanInstance, interfaceInfo.getInterfaceName(), tx);
570     }
571
572     public void installEntriesForFirstInterfaceonDpn(ElanInstance elanInfo, InterfaceInfo interfaceInfo, DpnInterfaces dpnInterfaces,
573                                                      boolean isFirstInterfaceInDpn, WriteTransaction tx) {
574         if (!isOperational(interfaceInfo)) {
575             return;
576         }
577         // LocalBroadcast Group creation with elan-Interfaces
578         setupLocalBroadcastGroups(elanInfo, dpnInterfaces, interfaceInfo);
579         if (isFirstInterfaceInDpn) {
580             logger.trace("waitTimeForSyncInstall is {}", waitTimeForSyncInstall);
581             BigInteger dpId = interfaceInfo.getDpId();
582             // RemoteBroadcast Group creation
583             try {
584                 Thread.sleep(waitTimeForSyncInstall);
585             } catch (InterruptedException e1) {
586                 logger.warn("Error while waiting for local BC group for ELAN {} to install", elanInfo);
587             }
588             setupElanBroadcastGroups(elanInfo, dpId);
589             try {
590                 Thread.sleep(waitTimeForSyncInstall);
591             } catch (InterruptedException e1) {
592                 logger.warn("Error while waiting for local BC group for ELAN {} to install", elanInfo);
593             }
594         }
595     }
596
597     public void setupFilterEqualsTable(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction writeFlowGroupTx) {
598         int ifTag = interfaceInfo.getInterfaceTag();
599         Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(ElanConstants.ELAN_FILTER_EQUALS_TABLE, ifTag),
600             9, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getTunnelIdMatchForFilterEqualsLPortTag(ifTag), ElanUtils.getInstructionsInPortForOutGroup(interfaceInfo.getInterfaceName()));
601
602         elanServiceProvider.getMdsalManager().addFlowToTx(interfaceInfo.getDpId(), flow, writeFlowGroupTx);
603
604         Flow flowEntry = MDSALUtil.buildFlowNew(ElanConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(ElanConstants.ELAN_FILTER_EQUALS_TABLE, 1000+ifTag),
605             10, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getMatchesForFilterEqualsLPortTag(ifTag),
606             MDSALUtil.buildInstructionsDrop());
607
608         elanServiceProvider.getMdsalManager().addFlowToTx(interfaceInfo.getDpId(), flowEntry, writeFlowGroupTx);
609     }
610
611     public void removeFilterEqualsTable(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction deleteFlowGroupTx) {
612         int ifTag = interfaceInfo.getInterfaceTag();
613         Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(ElanConstants.ELAN_FILTER_EQUALS_TABLE, ifTag),
614             9, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getTunnelIdMatchForFilterEqualsLPortTag(ifTag), ElanUtils.getInstructionsInPortForOutGroup(interfaceInfo.getInterfaceName()));
615
616         elanServiceProvider.getMdsalManager().removeFlowToTx(interfaceInfo.getDpId(), flow, deleteFlowGroupTx);
617
618         Flow flowEntity = MDSALUtil.buildFlowNew(ElanConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(ElanConstants.ELAN_FILTER_EQUALS_TABLE, 1000+ifTag),
619             10, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getMatchesForFilterEqualsLPortTag(ifTag),
620             MDSALUtil.buildInstructionsDrop());
621
622         elanServiceProvider.getMdsalManager().removeFlowToTx(interfaceInfo.getDpId(), flowEntity, deleteFlowGroupTx);
623     }
624
625     private List<Bucket> getRemoteBCGroupBucketInfos(ElanInstance elanInfo,
626                                                      int bucketKeyStart, InterfaceInfo interfaceInfo) {
627         BigInteger dpnId = interfaceInfo.getDpId();
628         int elanTag = elanInfo.getElanTag().intValue();
629         int bucketId = bucketKeyStart;
630         List<Bucket> listBuckets = new ArrayList<Bucket>();
631         ElanDpnInterfacesList elanDpns = ElanUtils.getElanDpnInterfacesList(elanInfo.getElanInstanceName());
632         if (elanDpns != null) {
633             List<DpnInterfaces> dpnInterfaceses = elanDpns.getDpnInterfaces();
634             for(DpnInterfaces dpnInterface : dpnInterfaceses) {
635                 if (ElanUtils.isDpnPresent(dpnInterface.getDpId()) && dpnInterface.getDpId() != dpnId && dpnInterface.getInterfaces() != null && !dpnInterface.getInterfaces().isEmpty()) {
636                     try {
637                         List<Action> listAction = ElanUtils.getInternalItmEgressAction(dpnId, dpnInterface.getDpId(), elanTag);
638                         listBuckets.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
639                         bucketId++;
640                     } catch (Exception ex) {
641                         logger.error( "Logical Group Interface not found between source Dpn - {}, destination Dpn - {} " ,dpnId, dpnInterface.getDpId() );
642                     }
643                 }
644             }
645             List<Bucket> elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId, bucketId);
646             listBuckets.addAll(elanL2GwDevicesBuckets);
647         }
648         return listBuckets;
649     }
650
651     private List<Bucket> getRemoteBCGroupBuckets(ElanInstance elanInfo, BigInteger dpnId, int bucketId) {
652         int elanTag = elanInfo.getElanTag().intValue();
653         List<Bucket> listBucketInfo = new ArrayList<Bucket>();
654         ElanDpnInterfacesList elanDpns = ElanUtils.getElanDpnInterfacesList(elanInfo.getElanInstanceName());
655         if (elanDpns != null) {
656             List<DpnInterfaces> dpnInterfaceses = elanDpns.getDpnInterfaces();
657             for(DpnInterfaces dpnInterface : dpnInterfaceses) {
658                 if (ElanUtils.isDpnPresent(dpnInterface.getDpId()) && dpnInterface.getDpId() != dpnId && dpnInterface.getInterfaces() != null && !dpnInterface.getInterfaces().isEmpty()) {
659                     try {
660                         List<Action> listActionInfo = ElanUtils.getInternalItmEgressAction(dpnId, dpnInterface.getDpId(), elanTag);
661                         if (listActionInfo.isEmpty()) {
662                             continue;
663                         }
664                         listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, 0, bucketId, 0xffffffffL, 0xffffffffL));
665                         bucketId++;
666                     } catch (Exception ex) {
667                         logger.error( "Logical Group Interface not found between source Dpn - {}, destination Dpn - {} " ,dpnId, dpnInterface.getDpId() );
668                     }
669                 }
670             }
671
672             List<Bucket> elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId, bucketId);
673             listBucketInfo.addAll(elanL2GwDevicesBuckets);
674         }
675         return listBucketInfo;
676     }
677
678     private void setElanBCGrouponOtherDpns(ElanInstance elanInfo,
679                                            BigInteger dpId, WriteTransaction tx) {
680         int elanTag = elanInfo.getElanTag().intValue();
681         long groupId = ElanUtils.getElanRemoteBCGID(elanTag);
682         List<Bucket> listBucket = new ArrayList<Bucket>();
683         int bucketId = 0;
684         ElanDpnInterfacesList elanDpns = ElanUtils.getElanDpnInterfacesList(elanInfo.getElanInstanceName());
685         if (elanDpns != null) {
686             List<DpnInterfaces> dpnInterfaceses = elanDpns.getDpnInterfaces();
687             for(DpnInterfaces dpnInterface : dpnInterfaceses) {
688                 List<Bucket> remoteListBucketInfo = new ArrayList<Bucket>();
689                 if (ElanUtils.isDpnPresent(dpnInterface.getDpId()) && !dpnInterface.getDpId().equals(dpId) && dpnInterface.getInterfaces() != null && !dpnInterface.getInterfaces().isEmpty()) {
690                     List<Action> listAction = new ArrayList<Action>();
691                     int actionKey = 0;
692                     listAction.add((new ActionInfo(ActionType.group, new String[] {String.valueOf(ElanUtils.getElanLocalBCGID(elanTag))}, ++actionKey)).buildAction());
693                     listBucket.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
694                     bucketId++;
695                     remoteListBucketInfo.addAll(listBucket);
696                     for(DpnInterfaces otherFes : dpnInterfaceses) {
697                         if (ElanUtils.isDpnPresent(otherFes.getDpId()) && otherFes.getDpId() != dpnInterface.getDpId()
698                             && otherFes.getInterfaces() != null && ! otherFes.getInterfaces().isEmpty()) {
699                             try {
700                                 List<Action> remoteListActionInfo = ElanUtils.getInternalItmEgressAction(dpnInterface.getDpId(), otherFes.getDpId(), elanTag);
701                                 remoteListBucketInfo.add(MDSALUtil.buildBucket(remoteListActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT,MDSALUtil.WATCH_GROUP));
702                                 bucketId++;
703                             } catch (Exception ex) {
704                                 logger.error( "Logical Group Interface not found between source Dpn - {}, destination Dpn - {} " ,dpnInterface.getDpId(), otherFes.getDpId() );
705                                 return;
706                             }
707                         }
708                     }
709                     List<Bucket> elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpId,
710                         bucketId);
711                     remoteListBucketInfo.addAll(elanL2GwDevicesBuckets);
712
713                     if (remoteListBucketInfo.size() == 0) {
714                         logger.debug( "No ITM is present on Dpn - {} " ,dpnInterface.getDpId());
715                         continue;
716                     }
717                     Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(remoteListBucketInfo));
718                     elanServiceProvider.getMdsalManager().addGroupToTx(dpnInterface.getDpId(), group, tx);
719                 }
720             }
721         }
722     }
723
724     /**
725      * Returns the bucket info with the given interface as the only bucket.
726      */
727     private Bucket getLocalBCGroupBucketInfo(InterfaceInfo interfaceInfo, int bucketIdStart) {
728         return MDSALUtil.buildBucket(getInterfacePortActions(interfaceInfo), MDSALUtil.GROUP_WEIGHT, bucketIdStart, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
729     }
730
731     private List<MatchInfo> getMatchesForElanTag(Long elanTag) {
732         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
733         // Matching metadata
734         mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
735             ElanUtils.getElanMetadataLabel(elanTag),
736             MetaDataUtil.METADATA_MASK_SERVICE }));
737         return mkMatches;
738     }
739
740
741     private List<MatchInfo> buildMatchesForVni(Long vni) {
742         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
743         MatchInfo match = new MatchInfo(MatchFieldType.tunnel_id,
744             new BigInteger[]{BigInteger.valueOf(vni)} );
745         mkMatches.add(match);
746         return mkMatches;
747     }
748
749     private List<Instruction> getInstructionsForOutGroup(
750         long groupId) {
751         List<Instruction> mkInstructions = new ArrayList<Instruction>();
752         List <Action> actions = new ArrayList <Action> ();
753         actions.add(new ActionInfo(ActionType.group, new String[]{Long.toString(groupId)}).buildAction());
754         mkInstructions.add(MDSALUtil.getWriteActionsInstruction(actions, 0));
755         return mkInstructions;
756     }
757
758     private List<MatchInfo> getMatchesForElanTag(long elanTag, boolean isSHFlagSet) {
759         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
760         // Matching metadata
761         mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
762             ElanUtils.getElanMetadataLabel(elanTag, isSHFlagSet),
763             MetaDataUtil.METADATA_MASK_SERVICE_SH_FLAG}));
764         return mkMatches;
765     }
766
767     /**
768      * Builds the list of instructions to be installed in the External Tunnel table (38), which so far
769      * consists in writing the elanTag in metadata and send packet to the new DHCP table
770      *
771      * @param elanTag elanTag to be written in metadata when flow is selected
772      * @return the instructions ready to be installed in a flow
773      */
774     private List<InstructionInfo> getInstructionsExtTunnelTable(Long elanTag) {
775         List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
776         mkInstructions.add(new InstructionInfo(InstructionType.write_metadata,
777             new BigInteger[] {
778                 ElanUtils.getElanMetadataLabel(elanTag),
779                 ElanUtils.getElanMetadataMask()
780             } ) );
781         // TODO: We should point to SMAC or DMAC depending on a configuration property to enable
782         // mac learning
783         mkInstructions.add(new InstructionInfo(InstructionType.goto_table,
784             new long[] { ElanConstants.ELAN_DMAC_TABLE }));
785
786         return mkInstructions;
787     }
788
789     // Install DMAC entry on dst DPN
790     public void installDMacAddressTables(ElanInstance elanInfo, InterfaceInfo interfaceInfo, BigInteger dstDpId) {
791         String interfaceName = interfaceInfo.getInterfaceName();
792         ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(interfaceName);
793         if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) {
794             WriteTransaction writeFlowTx = elanServiceProvider.getBroker().newWriteOnlyTransaction();
795             List<MacEntry> macEntries =  elanInterfaceMac.getMacEntry();
796             for(MacEntry macEntry : macEntries) {
797                 PhysAddress physAddress = macEntry.getMacAddress();
798                 ElanUtils.setupDMacFlowonRemoteDpn(elanInfo, interfaceInfo, dstDpId, physAddress.getValue(), writeFlowTx);
799             }
800             writeFlowTx.submit();
801         }
802     }
803
804     public void setupElanBroadcastGroups(ElanInstance elanInfo, BigInteger dpnId) {
805         List<Bucket> listBucket = new ArrayList<Bucket>();
806         int bucketId = 0;
807         int actionKey = 0;
808         Long elanTag = elanInfo.getElanTag();
809         long groupId = ElanUtils.getElanRemoteBCGID(elanTag);
810         List<Action> listAction = new ArrayList<Action>();
811         listAction.add((new ActionInfo(ActionType.group, new String[] {String.valueOf(ElanUtils.getElanLocalBCGID(elanTag))}, ++actionKey)).buildAction());
812         listBucket.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
813         bucketId++;
814         List<Bucket> listBucketInfoRemote = getRemoteBCGroupBuckets(elanInfo, dpnId, bucketId);
815         listBucket.addAll(listBucketInfoRemote);
816         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBucket));
817         logger.trace("Installing the remote BroadCast Group:{}", group);
818         elanServiceProvider.getMdsalManager().syncInstallGroup(dpnId, group, ElanConstants.DELAY_TIME_IN_MILLISECOND);
819     }
820
821     public void setupLocalBroadcastGroups(ElanInstance elanInfo, DpnInterfaces newDpnInterface, InterfaceInfo interfaceInfo) {
822         List<Bucket> listBucket = new ArrayList<Bucket>();
823         int bucketId = 0;
824         BigInteger dpnId = interfaceInfo.getDpId();
825         long groupId = ElanUtils.getElanLocalBCGID(elanInfo.getElanTag());
826
827         List<String> interfaces = new ArrayList<String>();
828         if (newDpnInterface != null) {
829             interfaces = newDpnInterface.getInterfaces();
830         }
831         for(String ifName : interfaces) {
832             // In case if there is a InterfacePort in the cache which is not in
833             // operational state, skip processing it
834             InterfaceInfo ifInfo = elanServiceProvider.getInterfaceManager().getInterfaceInfoFromOperationalDataStore(ifName, interfaceInfo.getInterfaceType());
835             if (!isOperational(ifInfo)) {
836                 continue;
837             }
838
839             listBucket.add(MDSALUtil.buildBucket(getInterfacePortActions(ifInfo), MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
840             bucketId++;
841         }
842
843         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBucket));
844         logger.trace("installing the localBroadCast Group:{}", group);
845         elanServiceProvider.getMdsalManager().syncInstallGroup(dpnId, group, ElanConstants.DELAY_TIME_IN_MILLISECOND);
846     }
847
848     public void removeLocalBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction deleteFlowGroupTx) {
849         BigInteger dpnId = interfaceInfo.getDpId();
850         long groupId = ElanUtils.getElanLocalBCGID(elanInfo.getElanTag());
851         List<Bucket> listBuckets = new ArrayList<>();
852         int bucketId = 0;
853         listBuckets.add(getLocalBCGroupBucketInfo(interfaceInfo, bucketId));
854         //listBuckets.addAll(getRemoteBCGroupBucketInfos(elanInfo, 1, interfaceInfo));
855         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBuckets));
856         logger.trace("deleted the localBroadCast Group:{}", group);
857         elanServiceProvider.getMdsalManager().removeGroupToTx(dpnId, group, deleteFlowGroupTx);
858     }
859
860     public void removeElanBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction deleteFlowGroupTx) {
861         int bucketId = 0;
862         int actionKey = 0;
863         Long elanTag = elanInfo.getElanTag();
864         List<Bucket> listBuckets = new ArrayList<>();
865         List<Action> listAction = new ArrayList<Action>();
866         listAction.add((new ActionInfo(ActionType.group, new String[] {String.valueOf(ElanUtils.getElanLocalBCGID(elanTag))}, ++actionKey)).buildAction());
867         listBuckets.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
868         bucketId++;
869         listBuckets.addAll(getRemoteBCGroupBucketInfos(elanInfo, bucketId, interfaceInfo));
870         BigInteger dpnId = interfaceInfo.getDpId();
871         long groupId = ElanUtils.getElanRemoteBCGID(elanInfo.getElanTag());
872         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBuckets));
873         logger.trace("deleting the remoteBroadCast group:{}", group);
874         elanServiceProvider.getMdsalManager().removeGroupToTx(dpnId, group, deleteFlowGroupTx);
875     }
876
877     /**
878      * Installs a flow in the External Tunnel table consisting in translating
879      * the VNI retrieved from the packet that came over a tunnel with a TOR into
880      * elanTag that will be used later in the ELANs pipeline.
881      *
882      * @param dpnId
883      *            the dpn id
884      * @param elanInfo
885      *            the elan info
886      */
887     public void setExternalTunnelTable(BigInteger dpnId, ElanInstance elanInfo) {
888         long elanTag = elanInfo.getElanTag();
889         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId,
890             NwConstants.EXTERNAL_TUNNEL_TABLE,
891             getFlowRef(NwConstants.EXTERNAL_TUNNEL_TABLE, elanTag),
892             5,  // prio
893             elanInfo.getElanInstanceName(),  // flowName
894             0,  // idleTimeout
895             0,  // hardTimeout
896             ITMConstants.COOKIE_ITM_EXTERNAL.add(BigInteger.valueOf(elanTag)),
897             buildMatchesForVni(elanInfo.getVni()),
898             getInstructionsExtTunnelTable(elanTag) );
899
900         elanServiceProvider.getMdsalManager().installFlow(flowEntity);
901     }
902
903     /**
904      * Removes, from External Tunnel table, the flow that translates from VNI to elanTag.
905      * Important: ensure this method is only called whenever there is no other ElanInterface in the specified DPN
906      *
907      * @param dpnId DPN whose Ext Tunnel table is going to be modified
908      * @param elanInfo holds the elanTag needed for selecting the flow to be removed
909      */
910     public void unsetExternalTunnelTable(BigInteger dpnId, ElanInstance elanInfo) {
911         // TODO: Use DataStoreJobCoordinator in order to avoid that removing the last ElanInstance plus
912         // adding a new one does (almost at the same time) are executed in that exact order
913
914         String flowId = getFlowRef(NwConstants.EXTERNAL_TUNNEL_TABLE, elanInfo.getElanTag());
915         FlowEntity flowEntity = new FlowEntity(dpnId);
916         flowEntity.setTableId(NwConstants.EXTERNAL_TUNNEL_TABLE);
917         flowEntity.setFlowId(flowId);
918         elanServiceProvider.getMdsalManager().removeFlow(flowEntity);
919     }
920
921     public void setupTerminateServiceTable(ElanInstance elanInfo, BigInteger dpId, WriteTransaction writeFlowGroupTx) {
922         long elanTag = elanInfo.getElanTag();
923         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE, getFlowRef(NwConstants.INTERNAL_TUNNEL_TABLE, elanTag),
924             5, String.format("%s:%d","ITM Flow Entry ",elanTag), 0,  0, ITMConstants.COOKIE_ITM.add(BigInteger.valueOf(elanTag)), ElanUtils.getTunnelMatchesForServiceId((int)elanTag),
925             getInstructionsForOutGroup(ElanUtils.getElanLocalBCGID(elanTag)));
926
927         elanServiceProvider.getMdsalManager().addFlowToTx(dpId, flowEntity, writeFlowGroupTx);
928     }
929
930     public void setupUnknownDMacTable(ElanInstance elanInfo, BigInteger dpId, WriteTransaction writeFlowGroupTx) {
931         long elanTag = elanInfo.getElanTag();
932         Flow flowEntity = MDSALUtil.buildFlowNew(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, getUnknownDmacFlowRef(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag, /*SH flag*/false),
933             5, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_UNKNOWN_DMAC.add(BigInteger.valueOf(elanTag)), getMatchesForElanTag(elanTag, /*SH flag*/false),
934             getInstructionsForOutGroup(ElanUtils.getElanRemoteBCGID(elanTag)));
935
936         elanServiceProvider.getMdsalManager().addFlowToTx(dpId, flowEntity, writeFlowGroupTx);
937
938         // only if vni is present in elanInfo, perform the following
939         if (elanInfo.getVni() != null && elanInfo.getVni() != 0) {
940             Flow flowEntity2 = MDSALUtil.buildFlowNew(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, getUnknownDmacFlowRef(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag, /*SH flag*/true),
941                 5, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_UNKNOWN_DMAC.add(BigInteger.valueOf(elanTag)), getMatchesForElanTag(elanTag, /*SH flag*/true),
942                 getInstructionsForOutGroup(ElanUtils.getElanLocalBCGID(elanTag)));
943             elanServiceProvider.getMdsalManager().addFlowToTx(dpId, flowEntity2, writeFlowGroupTx);
944         }
945
946     }
947
948     private void removeUnknownDmacFlow(BigInteger dpId, ElanInstance elanInfo, WriteTransaction deleteFlowGroupTx) {
949         //        Flow flow = getUnknownDmacFlowEntity(dpId, elanInfo);
950         //        mdsalManager.removeFlow(dpId, flow);
951         Flow flow = new FlowBuilder().setId(new FlowId(getUnknownDmacFlowRef(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE,
952             elanInfo.getElanTag(), /*SH flag*/ false)))
953             .setTableId(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE)
954             .build();
955         elanServiceProvider.getMdsalManager().removeFlowToTx(dpId, flow, deleteFlowGroupTx);
956
957         if ( elanInfo.getVni() != null && elanInfo.getVni() > 0 ) {
958             Flow flow2 = new FlowBuilder().setId(new FlowId(getUnknownDmacFlowRef(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE,
959                 elanInfo.getElanTag(), /*SH flag*/ true)))
960                 .setTableId(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE)
961                 .build();
962             elanServiceProvider.getMdsalManager().removeFlowToTx(dpId, flow2, deleteFlowGroupTx);
963         }
964
965
966     }
967
968     private void removeDefaultTermFlow(BigInteger dpId, long elanTag) {
969         ElanUtils.removeTerminatingServiceAction(dpId, (int) elanTag);
970     }
971
972     private void bindService(ElanInstance elanInfo, String interfaceName, WriteTransaction tx) {
973         // interfaceManager.bindService(interfaceName, ElanUtils.getServiceInfo(elanInfo.getElanInstanceName(), elanInfo.getElanTag(), interfaceName));
974
975         int priority = ElanConstants.ELAN_SERVICE_PRIORITY;
976         int instructionKey = 0;
977         List<Instruction> instructions = new ArrayList<Instruction>();
978         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(ElanUtils.getElanMetadataLabel(elanInfo.getElanTag()), MetaDataUtil.METADATA_MASK_SERVICE, ++instructionKey));
979         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(ElanConstants.ELAN_SMAC_TABLE, ++instructionKey));
980         BoundServices
981             serviceInfo =
982             ElanUtils.getBoundServices(String.format("%s.%s.%s", "vpn",elanInfo.getElanInstanceName(), interfaceName),
983                 ElanConstants.ELAN_SERVICE_INDEX, priority,
984                 ElanConstants.COOKIE_ELAN_INGRESS_TABLE, instructions);
985         tx.put(LogicalDatastoreType.CONFIGURATION,
986             ElanUtils.buildServiceId(interfaceName, ElanConstants.ELAN_SERVICE_INDEX), serviceInfo, true);
987     }
988
989     private void unbindService(ElanInstance elanInfo, String interfaceName, WriteTransaction tx) {
990         tx.delete(LogicalDatastoreType.CONFIGURATION, ElanUtils.buildServiceId(interfaceName,ElanConstants.ELAN_SERVICE_INDEX));
991     }
992
993     private String getFlowRef(long tableId, long elanTag) {
994         return new StringBuffer().append(tableId).append(elanTag).toString();
995     }
996
997     private String getUnknownDmacFlowRef(long tableId, long elanTag, boolean shFlag) {
998         return new StringBuffer().append(tableId).append(elanTag).append(shFlag).toString();
999     }
1000
1001     private List<Action> getInterfacePortActions(InterfaceInfo interfaceInfo) {
1002         List<Action> listAction = new ArrayList<Action>();
1003         int actionKey = 0;
1004         listAction.add((new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {BigInteger.valueOf(interfaceInfo.getInterfaceTag())}, actionKey)).buildAction());
1005         actionKey++;
1006         listAction.add((new ActionInfo(ActionType.nx_resubmit,
1007             new String[] {String.valueOf(ElanConstants.ELAN_FILTER_EQUALS_TABLE)}, actionKey)).buildAction());
1008         return listAction;
1009     }
1010
1011     private DpnInterfaces updateElanDpnInterfacesList(String elanInstanceName, BigInteger dpId, List<String> interfaceNames, WriteTransaction tx) {
1012         DpnInterfaces dpnInterface = new DpnInterfacesBuilder().setDpId(dpId)
1013             .setInterfaces(interfaceNames).setKey(new DpnInterfacesKey(dpId)).build();
1014         tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId),
1015             dpnInterface, true);
1016         return dpnInterface;
1017     }
1018
1019     /**
1020      * Delete elan dpn interface from operational DS.
1021      *
1022      * @param elanInstanceName
1023      *            the elan instance name
1024      * @param dpId
1025      *            the dp id
1026      */
1027     private void deleteElanDpnInterface(String elanInstanceName, BigInteger dpId, WriteTransaction tx) {
1028         tx.delete(LogicalDatastoreType.OPERATIONAL,
1029             ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId));
1030     }
1031
1032     private DpnInterfaces createElanInterfacesList(String elanInstanceName, String interfaceName, BigInteger dpId, WriteTransaction tx) {
1033         List<String> interfaceNames = new ArrayList<String>();
1034         interfaceNames.add(interfaceName);
1035         DpnInterfaces dpnInterface = new DpnInterfacesBuilder().setDpId(dpId)
1036             .setInterfaces(interfaceNames).setKey(new DpnInterfacesKey(dpId)).build();
1037         tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId),
1038             dpnInterface, true);
1039         return dpnInterface;
1040     }
1041
1042     private void createElanInterfaceTablesList(String interfaceName, WriteTransaction tx) {
1043         InstanceIdentifier<ElanInterfaceMac> elanInterfaceMacTables = ElanUtils.getElanInterfaceMacEntriesOperationalDataPath(interfaceName);
1044         Optional<ElanInterfaceMac> interfaceMacTables = ElanUtils.read(elanServiceProvider.getBroker(), LogicalDatastoreType.OPERATIONAL, elanInterfaceMacTables);
1045         // Adding new Elan Interface Port to the operational DataStore without Static-Mac Entries..
1046         if (!interfaceMacTables.isPresent()) {
1047             ElanInterfaceMac elanInterfaceMacTable = new ElanInterfaceMacBuilder().setElanInterface(interfaceName).setKey(new ElanInterfaceMacKey(interfaceName)).build();
1048             tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInterfaceMacEntriesOperationalDataPath(interfaceName),
1049                 elanInterfaceMacTable, true);
1050         }
1051     }
1052
1053     private void createElanStateList(String elanInstanceName, String interfaceName, WriteTransaction tx) {
1054         InstanceIdentifier<Elan> elanInstance = ElanUtils.getElanInstanceOperationalDataPath(elanInstanceName);
1055         Optional<Elan> elanInterfaceLists = ElanUtils.read(elanServiceProvider.getBroker(), LogicalDatastoreType.OPERATIONAL, elanInstance);
1056         // Adding new Elan Interface Port to the operational DataStore without Static-Mac Entries..
1057         if (elanInterfaceLists.isPresent()) {
1058             List<String> interfaceLists = elanInterfaceLists.get().getElanInterfaces();
1059             if (interfaceLists == null) {
1060                 interfaceLists = new ArrayList<>();
1061             }
1062             interfaceLists.add(interfaceName);
1063             Elan elanState = new ElanBuilder().setName(elanInstanceName).setElanInterfaces(interfaceLists).setKey(new ElanKey(elanInstanceName)).build();
1064             tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanInstanceName), elanState, true);
1065         }
1066     }
1067
1068     private boolean isOperational(InterfaceInfo interfaceInfo) {
1069         if (interfaceInfo == null) {
1070             return false;
1071         }
1072         return interfaceInfo.getAdminState() == InterfaceInfo.InterfaceAdminState.ENABLED;
1073     }
1074
1075     public void handleInternalTunnelStateEvent(BigInteger srcDpId, BigInteger dstDpId) {
1076         ElanDpnInterfaces dpnInterfaceLists =  ElanUtils.getElanDpnInterfacesList();
1077         if (dpnInterfaceLists == null) {
1078             return;
1079         }
1080         List<ElanDpnInterfacesList> elanDpnIf = dpnInterfaceLists.getElanDpnInterfacesList();
1081         for(ElanDpnInterfacesList elanDpns: elanDpnIf) {
1082             int cnt = 0;
1083             String elanName = elanDpns.getElanInstanceName();
1084             List<DpnInterfaces> dpnInterfaces = elanDpns.getDpnInterfaces();
1085             if (dpnInterfaces == null) {
1086                 continue;
1087             }
1088             for (DpnInterfaces dpnIf : dpnInterfaces) {
1089                 if (dpnIf.getDpId().equals(srcDpId) || dpnIf.getDpId().equals(dstDpId)) {
1090                     cnt++;
1091                 }
1092             }
1093             if (cnt == 2) {
1094                 logger.debug("Elan instance:{} is present b/w srcDpn:{} and dstDpn:{}", elanName, srcDpId, dstDpId);
1095                 ElanInstance elanInfo = ElanUtils.getElanInstanceByName(elanName);
1096                 // update Remote BC Group
1097                 setupElanBroadcastGroups(elanInfo, srcDpId);
1098
1099                 DpnInterfaces dpnInterface = ElanUtils.getElanInterfaceInfoByElanDpn(elanName, dstDpId);
1100                 Set<String> interfaceLists = new HashSet<>();
1101                 interfaceLists.addAll(dpnInterface.getInterfaces());
1102                 for(String ifName : interfaceLists) {
1103                     InterfaceInfo interfaceInfo = elanServiceProvider.getInterfaceManager().getInterfaceInfo(ifName);
1104                     if (isOperational(interfaceInfo)) {
1105                         installDMacAddressTables(elanInfo, interfaceInfo, srcDpId);
1106                     }
1107                 }
1108             }
1109
1110         }
1111     }
1112
1113     /**
1114      * Handle external tunnel state event.
1115      *
1116      * @param externalTunnel
1117      *            the external tunnel
1118      * @param intrf
1119      *            the interface
1120      */
1121     public void handleExternalTunnelStateEvent(ExternalTunnel externalTunnel, Interface intrf) {
1122         if (!validateExternalTunnelStateEvent(externalTunnel, intrf)) {
1123             return;
1124         }
1125         // dpId/externalNodeId will be available either in source or destination
1126         // based on the tunnel end point
1127         BigInteger dpId = null;
1128         NodeId externalNodeId = null;
1129         if (StringUtils.isNumeric(externalTunnel.getSourceDevice())) {
1130             dpId = new BigInteger(externalTunnel.getSourceDevice());
1131             externalNodeId = new NodeId(externalTunnel.getDestinationDevice());
1132         } else if (StringUtils.isNumeric(externalTunnel.getDestinationDevice())) {
1133             dpId = new BigInteger(externalTunnel.getDestinationDevice());
1134             externalNodeId = new NodeId(externalTunnel.getSourceDevice());
1135         }
1136         if (dpId == null || externalNodeId == null) {
1137             logger.error("Dp ID / externalNodeId not found in external tunnel {}", externalTunnel);
1138             return;
1139         }
1140
1141         ElanDpnInterfaces dpnInterfaceLists = ElanUtils.getElanDpnInterfacesList();
1142         if (dpnInterfaceLists == null) {
1143             return;
1144         }
1145         List<ElanDpnInterfacesList> elanDpnIf = dpnInterfaceLists.getElanDpnInterfacesList();
1146         for (ElanDpnInterfacesList elanDpns : elanDpnIf) {
1147             String elanName = elanDpns.getElanInstanceName();
1148             ElanInstance elanInfo = ElanUtils.getElanInstanceByName(elanName);
1149
1150             DpnInterfaces dpnInterfaces = ElanUtils.getElanInterfaceInfoByElanDpn(elanName, dpId);
1151             if (dpnInterfaces == null || dpnInterfaces.getInterfaces() == null
1152                 || dpnInterfaces.getInterfaces().isEmpty()) {
1153                 continue;
1154             }
1155             logger.debug("Elan instance:{} is present in Dpn:{} ", elanName, dpId);
1156
1157             setupElanBroadcastGroups(elanInfo, dpId);
1158             // install L2gwDevices local macs in dpn.
1159             ElanL2GatewayUtils.installL2gwDeviceMacsInDpn(dpId, externalNodeId, elanInfo);
1160             // Install dpn macs on external device
1161             ElanL2GatewayUtils.installDpnMacsInL2gwDevice(elanName, new HashSet<>(dpnInterfaces.getInterfaces()), dpId,
1162                 externalNodeId);
1163         }
1164         logger.info("Handled ExternalTunnelStateEvent for {}", externalTunnel);
1165     }
1166
1167     /**
1168      * Validate external tunnel state event.
1169      *
1170      * @param externalTunnel
1171      *            the external tunnel
1172      * @param intrf
1173      *            the intrf
1174      * @return true, if successful
1175      */
1176     private boolean validateExternalTunnelStateEvent(ExternalTunnel externalTunnel, Interface intrf) {
1177         if (intrf.getOperStatus() == Interface.OperStatus.Up) {
1178             String srcDevice = externalTunnel.getDestinationDevice();
1179             String destDevice = externalTunnel.getSourceDevice();
1180             ExternalTunnel otherEndPointExtTunnel = ElanUtils.getExternalTunnel(srcDevice, destDevice,
1181                 LogicalDatastoreType.CONFIGURATION);
1182             if (logger.isTraceEnabled()) {
1183                 logger.trace("Validating external tunnel state: src tunnel {}, dest tunnel {}", externalTunnel,
1184                     otherEndPointExtTunnel);
1185             }
1186             if (otherEndPointExtTunnel != null) {
1187                 boolean otherEndPointInterfaceOperational = ElanUtils
1188                     .isInterfaceOperational(otherEndPointExtTunnel.getTunnelInterfaceName(), elanServiceProvider.getBroker());
1189                 if (otherEndPointInterfaceOperational) {
1190                     return true;
1191                 } else {
1192                     logger.debug("Other end [{}] of the external tunnel is not yet UP for {}",
1193                         otherEndPointExtTunnel.getTunnelInterfaceName(), externalTunnel);
1194                 }
1195             }
1196         }
1197         return false;
1198     }
1199
1200     private List<MatchInfo> getMatchesForFilterEqualsLPortTag(int LportTag) {
1201         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
1202         // Matching metadata
1203         mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
1204             MetaDataUtil.getLportTagMetaData(LportTag),
1205             MetaDataUtil.METADATA_MASK_LPORT_TAG }));
1206         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(LportTag)}));
1207         return mkMatches;
1208     }
1209
1210
1211     private List<MatchInfo> getTunnelIdMatchForFilterEqualsLPortTag(int LportTag) {
1212         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
1213         // Matching metadata
1214         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {
1215             BigInteger.valueOf(LportTag)}));
1216         return mkMatches;
1217     }
1218
1219     public void updateRemoteBroadcastGroupForAllElanDpns(ElanInstance elanInfo) {
1220         List<DpnInterfaces> dpns = ElanUtils.getInvolvedDpnsInElan(elanInfo.getElanInstanceName());
1221         if (dpns == null) {
1222             return;
1223         }
1224         for (DpnInterfaces dpn : dpns) {
1225             setupElanBroadcastGroups(elanInfo, dpn.getDpId());
1226         }
1227     }
1228
1229     public static List<Bucket> getRemoteBCGroupBucketsOfElanL2GwDevices(ElanInstance elanInfo, BigInteger dpnId,
1230                                                                         int bucketId) {
1231         List<Bucket> listBucketInfo = new ArrayList<Bucket>();
1232         ConcurrentMap<String, L2GatewayDevice> map = ElanL2GwCacheUtils
1233             .getInvolvedL2GwDevices(elanInfo.getElanInstanceName());
1234         for (L2GatewayDevice device : map.values()) {
1235             String interfaceName = ElanL2GatewayUtils.getExternalTunnelInterfaceName(String.valueOf(dpnId),
1236                 device.getHwvtepNodeId());
1237             if (interfaceName == null) {
1238                 continue;
1239             }
1240             List<Action> listActionInfo = ElanUtils.buildItmEgressActions(interfaceName, elanInfo.getVni());
1241             listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId,
1242                 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
1243             bucketId++;
1244         }
1245         return listBucketInfo;
1246     }
1247
1248     public ElanServiceProvider getElanServiceProvider() {
1249         return elanServiceProvider;
1250     }
1251
1252     public void setElanServiceProvider(ElanServiceProvider elanServiceProvider) {
1253         this.elanServiceProvider = elanServiceProvider;
1254     }
1255
1256
1257     @Override
1258     protected ElanInterfaceManager getDataTreeChangeListener() {
1259         return this;
1260     }
1261
1262 }
1263
1264