0c0c0f5446d2c9cd7741bb47c4dc3a5c070bd5f4
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / internal / ElanInterfaceManager.java
1 /*
2  * Copyright (c) 2016, 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.elan.internal;
9
10 import static org.opendaylight.netvirt.elan.utils.ElanUtils.isVxlanNetworkOrVxlanSegment;
11
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.Lists;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Objects;
23 import java.util.Queue;
24 import java.util.Set;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentLinkedQueue;
27 import javax.annotation.PostConstruct;
28 import javax.inject.Inject;
29 import javax.inject.Singleton;
30 import org.apache.commons.lang3.StringUtils;
31 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
32 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
33 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
34 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
35 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
36 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
37 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
38 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
39 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
40 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
41 import org.opendaylight.genius.itm.globals.ITMConstants;
42 import org.opendaylight.genius.mdsalutil.FlowEntity;
43 import org.opendaylight.genius.mdsalutil.FlowEntityBuilder;
44 import org.opendaylight.genius.mdsalutil.InstructionInfo;
45 import org.opendaylight.genius.mdsalutil.MDSALUtil;
46 import org.opendaylight.genius.mdsalutil.MatchInfo;
47 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
48 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
49 import org.opendaylight.genius.mdsalutil.NwConstants;
50 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
51 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
52 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
53 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
54 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
55 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
56 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteActions;
57 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
58 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
59 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
60 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
61 import org.opendaylight.genius.srm.RecoverableListener;
62 import org.opendaylight.genius.srm.ServiceRecoveryRegistry;
63 import org.opendaylight.genius.utils.ServiceIndex;
64 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
65 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
66 import org.opendaylight.netvirt.elan.ElanException;
67 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
68 import org.opendaylight.netvirt.elan.cache.ElanInterfaceCache;
69 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayMulticastUtils;
70 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
71 import org.opendaylight.netvirt.elan.recovery.impl.ElanServiceRecoveryHandler;
72 import org.opendaylight.netvirt.elan.utils.ElanConstants;
73 import org.opendaylight.netvirt.elan.utils.ElanEtreeUtils;
74 import org.opendaylight.netvirt.elan.utils.ElanForwardingEntriesHandler;
75 import org.opendaylight.netvirt.elan.utils.ElanItmUtils;
76 import org.opendaylight.netvirt.elan.utils.ElanUtils;
77 import org.opendaylight.netvirt.elanmanager.api.ElanHelper;
78 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
79 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
80 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
81 import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
82 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
83 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
84 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnel;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInstance;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface.EtreeInterfaceType;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeLeafTagName;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanForwardingTables;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMacBuilder;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMacKey;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfacesBuilder;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfacesKey;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.forwarding.tables.MacTable;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.forwarding.tables.MacTableKey;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceBuilder;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.elan._interface.StaticMacEntries;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.state.Elan;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.state.ElanBuilder;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.state.ElanKey;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntry;
120 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntryBuilder;
121 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntryKey;
122 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
123 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
124 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
125 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
126 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
127 import org.slf4j.Logger;
128 import org.slf4j.LoggerFactory;
129
130 /**
131  * Class in charge of handling creations, modifications and removals of
132  * ElanInterfaces.
133  *
134  * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface
135  */
136 @Singleton
137 public class ElanInterfaceManager extends AsyncDataTreeChangeListenerBase<ElanInterface, ElanInterfaceManager>
138         implements RecoverableListener {
139     private static final Logger LOG = LoggerFactory.getLogger(ElanInterfaceManager.class);
140     private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
141     private static final boolean SH_FLAG_SET = true;
142     private static final boolean SH_FLAG_UNSET = false;
143
144     private final DataBroker broker;
145     private final ManagedNewTransactionRunner txRunner;
146     private final IMdsalApiManager mdsalManager;
147     private final IInterfaceManager interfaceManager;
148     private final IdManagerService idManager;
149     private final ElanForwardingEntriesHandler elanForwardingEntriesHandler;
150     private final INeutronVpnManager neutronVpnManager;
151     private final ElanItmUtils elanItmUtils;
152     private final ElanEtreeUtils elanEtreeUtils;
153     private final ElanL2GatewayUtils elanL2GatewayUtils;
154     private final ElanL2GatewayMulticastUtils elanL2GatewayMulticastUtils;
155     private final ElanUtils elanUtils;
156     private final JobCoordinator jobCoordinator;
157     private final ElanInstanceCache elanInstanceCache;
158     private final ElanInterfaceCache elanInterfaceCache;
159
160     private final Map<String, ConcurrentLinkedQueue<ElanInterface>>
161         unProcessedElanInterfaces = new ConcurrentHashMap<>();
162
163     @Inject
164     public ElanInterfaceManager(final DataBroker dataBroker, final IdManagerService managerService,
165                                 final IMdsalApiManager mdsalApiManager, IInterfaceManager interfaceManager,
166                                 final ElanForwardingEntriesHandler elanForwardingEntriesHandler,
167                                 final INeutronVpnManager neutronVpnManager, final ElanItmUtils elanItmUtils,
168                                 final ElanEtreeUtils elanEtreeUtils, final ElanL2GatewayUtils elanL2GatewayUtils,
169                                 final ElanUtils elanUtils, final JobCoordinator jobCoordinator,
170                                 final ElanL2GatewayMulticastUtils elanL2GatewayMulticastUtils,
171                                 final ElanInstanceCache elanInstanceCache,
172                                 final ElanInterfaceCache elanInterfaceCache,
173                                 final ElanServiceRecoveryHandler elanServiceRecoveryHandler,
174                                 final ServiceRecoveryRegistry serviceRecoveryRegistry) {
175         super(ElanInterface.class, ElanInterfaceManager.class);
176         this.broker = dataBroker;
177         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
178         this.idManager = managerService;
179         this.mdsalManager = mdsalApiManager;
180         this.interfaceManager = interfaceManager;
181         this.elanForwardingEntriesHandler = elanForwardingEntriesHandler;
182         this.neutronVpnManager = neutronVpnManager;
183         this.elanItmUtils = elanItmUtils;
184         this.elanEtreeUtils = elanEtreeUtils;
185         this.elanL2GatewayUtils = elanL2GatewayUtils;
186         this.elanUtils = elanUtils;
187         this.jobCoordinator = jobCoordinator;
188         this.elanL2GatewayMulticastUtils = elanL2GatewayMulticastUtils;
189         this.elanInstanceCache = elanInstanceCache;
190         this.elanInterfaceCache = elanInterfaceCache;
191         serviceRecoveryRegistry.addRecoverableListener(elanServiceRecoveryHandler.buildServiceRegistryKey(), this);
192     }
193
194     @Override
195     @PostConstruct
196     public void init() {
197         registerListener();
198     }
199
200     @Override
201     public void registerListener() {
202         registerListener(LogicalDatastoreType.CONFIGURATION, broker);
203     }
204
205     @Override
206     protected InstanceIdentifier<ElanInterface> getWildCardPath() {
207         return InstanceIdentifier.create(ElanInterfaces.class).child(ElanInterface.class);
208     }
209
210     @Override
211     protected void remove(InstanceIdentifier<ElanInterface> identifier, ElanInterface del) {
212         String interfaceName = del.getName();
213         ElanInstance elanInfo = elanInstanceCache.get(del.getElanInstanceName()).orNull();
214         /*
215          * Handling in case the elan instance is deleted.If the Elan instance is
216          * deleted, there is no need to explicitly delete the elan interfaces
217          */
218         if (elanInfo == null) {
219             return;
220         }
221         InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
222         if (interfaceInfo == null && elanInfo.isExternal()) {
223             // In deleting external network, the underlying ietf Inteface might have been removed
224             // from the config DS prior to deleting the ELAN interface. We try to get the InterfaceInfo
225             // from Operational DS instead
226             interfaceInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
227         }
228         String elanInstanceName = elanInfo.getElanInstanceName();
229         InterfaceRemoveWorkerOnElan configWorker = new InterfaceRemoveWorkerOnElan(elanInstanceName, elanInfo,
230                 interfaceName, interfaceInfo, this);
231         jobCoordinator.enqueueJob(elanInstanceName, configWorker, ElanConstants.JOB_MAX_RETRIES);
232     }
233
234     @SuppressWarnings("checkstyle:ForbiddenMethod")
235     public List<ListenableFuture<Void>> removeElanInterface(ElanInstance elanInfo, String interfaceName,
236             InterfaceInfo interfaceInfo) {
237         String elanName = elanInfo.getElanInstanceName();
238         boolean isLastElanInterface = false;
239         boolean isLastInterfaceOnDpn = false;
240         BigInteger dpId = null;
241         long elanTag = elanInfo.getElanTag();
242         // We use two transaction so we don't suffer on multiple shards (interfaces and flows)
243         WriteTransaction interfaceTx = broker.newWriteOnlyTransaction();
244         Elan elanState = removeElanStateForInterface(elanInfo, interfaceName, interfaceTx);
245         if (elanState == null) {
246             interfaceTx.cancel();
247             return Collections.emptyList();
248         }
249         WriteTransaction flowTx = broker.newWriteOnlyTransaction();
250         List<String> elanInterfaces = elanState.getElanInterfaces();
251         if (elanInterfaces.isEmpty()) {
252             isLastElanInterface = true;
253         }
254         if (interfaceInfo != null) {
255             dpId = interfaceInfo.getDpId();
256             DpnInterfaces dpnInterfaces = removeElanDpnInterfaceFromOperationalDataStore(elanName, dpId,
257                     interfaceName, elanTag, interfaceTx);
258             /*
259              * If there are not elan ports, remove the unknown dmac, terminating
260              * service table flows, remote/local bc group
261              */
262             if (dpnInterfaces == null || dpnInterfaces.getInterfaces() == null
263                     || dpnInterfaces.getInterfaces().isEmpty()) {
264                 // No more Elan Interfaces in this DPN
265                 LOG.debug("deleting the elan: {} present on dpId: {}", elanInfo.getElanInstanceName(), dpId);
266                 if (!elanUtils.isOpenstackVniSemanticsEnforced()) {
267                     removeDefaultTermFlow(dpId, elanInfo.getElanTag());
268                 }
269                 removeUnknownDmacFlow(dpId, elanInfo, flowTx, elanInfo.getElanTag());
270                 removeEtreeUnknownDmacFlow(dpId, elanInfo, flowTx);
271                 removeElanBroadcastGroup(elanInfo, interfaceInfo, flowTx);
272                 removeLocalBroadcastGroup(elanInfo, interfaceInfo, flowTx);
273                 removeEtreeBroadcastGrups(elanInfo, interfaceInfo, flowTx);
274                 if (isVxlanNetworkOrVxlanSegment(elanInfo)) {
275                     if (elanUtils.isOpenstackVniSemanticsEnforced()) {
276                         elanUtils.removeTerminatingServiceAction(dpId,
277                                 elanUtils.getVxlanSegmentationId(elanInfo).intValue());
278                     }
279                     unsetExternalTunnelTable(dpId, elanInfo);
280                 }
281                 isLastInterfaceOnDpn = true;
282             } else {
283                 setupLocalBroadcastGroups(elanInfo, dpnInterfaces, interfaceInfo);
284             }
285         }
286
287         List<ListenableFuture<Void>> futures = new ArrayList<>();
288         futures.add(ElanUtils.waitForTransactionToComplete(interfaceTx));
289         futures.add(ElanUtils.waitForTransactionToComplete(flowTx));
290
291         if (isLastInterfaceOnDpn && dpId != null && isVxlanNetworkOrVxlanSegment(elanInfo)) {
292             setElanAndEtreeBCGrouponOtherDpns(elanInfo, dpId);
293         }
294         InterfaceRemoveWorkerOnElanInterface removeInterfaceWorker = new InterfaceRemoveWorkerOnElanInterface(
295                 interfaceName, elanInfo, interfaceInfo, this, isLastElanInterface);
296         jobCoordinator.enqueueJob(ElanUtils.getElanInterfaceJobKey(interfaceName), removeInterfaceWorker,
297                 ElanConstants.JOB_MAX_RETRIES);
298
299         return futures;
300     }
301
302     private void removeEtreeUnknownDmacFlow(BigInteger dpId, ElanInstance elanInfo,
303             WriteTransaction deleteFlowGroupTx) {
304         EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanInfo.getElanTag());
305         if (etreeLeafTag != null) {
306             long leafTag = etreeLeafTag.getEtreeLeafTag().getValue();
307             removeUnknownDmacFlow(dpId, elanInfo, deleteFlowGroupTx, leafTag);
308         }
309     }
310
311     private void removeEtreeBroadcastGrups(ElanInstance elanInfo, InterfaceInfo interfaceInfo,
312             WriteTransaction deleteFlowGroupTx) {
313         removeLeavesEtreeBroadcastGroup(elanInfo, interfaceInfo, deleteFlowGroupTx);
314         removeLeavesLocalBroadcastGroup(elanInfo, interfaceInfo, deleteFlowGroupTx);
315     }
316
317     private void removeLeavesLocalBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo,
318             WriteTransaction deleteFlowGroupTx) {
319         EtreeInstance etreeInstance = elanInfo.augmentation(EtreeInstance.class);
320         if (etreeInstance != null) {
321             BigInteger dpnId = interfaceInfo.getDpId();
322             long groupId = ElanUtils.getEtreeLeafLocalBCGId(etreeInstance.getEtreeLeafTagVal().getValue());
323             List<Bucket> listBuckets = new ArrayList<>();
324             int bucketId = 0;
325             listBuckets.add(getLocalBCGroupBucketInfo(interfaceInfo, bucketId));
326             Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
327                     MDSALUtil.buildBucketLists(listBuckets));
328             LOG.trace("deleted the localBroadCast Group:{}", group);
329             mdsalManager.removeGroupToTx(dpnId, group, deleteFlowGroupTx);
330         }
331     }
332
333     private void removeLeavesEtreeBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo,
334             WriteTransaction deleteFlowGroupTx) {
335         EtreeInstance etreeInstance = elanInfo.augmentation(EtreeInstance.class);
336         if (etreeInstance != null) {
337             long etreeTag = etreeInstance.getEtreeLeafTagVal().getValue();
338             int bucketId = 0;
339             int actionKey = 0;
340             List<Bucket> listBuckets = new ArrayList<>();
341             List<Action> listAction = new ArrayList<>();
342             listAction.add(new ActionGroup(ElanUtils.getEtreeLeafLocalBCGId(etreeTag)).buildAction(++actionKey));
343             listBuckets.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT,
344                     MDSALUtil.WATCH_GROUP));
345             bucketId++;
346             listBuckets.addAll(getRemoteBCGroupBucketInfos(elanInfo, bucketId, interfaceInfo, etreeTag));
347             BigInteger dpnId = interfaceInfo.getDpId();
348             long groupId = ElanUtils.getEtreeLeafRemoteBCGId(etreeTag);
349             Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
350                     MDSALUtil.buildBucketLists(listBuckets));
351             LOG.trace("deleting the remoteBroadCast group:{}", group);
352             mdsalManager.removeGroupToTx(dpnId, group, deleteFlowGroupTx);
353         }
354     }
355
356     private Elan removeElanStateForInterface(ElanInstance elanInfo, String interfaceName, WriteTransaction tx) {
357         String elanName = elanInfo.getElanInstanceName();
358         Elan elanState = ElanUtils.getElanByName(broker, elanName);
359         if (elanState == null) {
360             return elanState;
361         }
362         List<String> elanInterfaces = elanState.getElanInterfaces();
363         boolean isRemoved = elanInterfaces.remove(interfaceName);
364         if (!isRemoved) {
365             return elanState;
366         }
367
368         if (elanInterfaces.isEmpty()) {
369             tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanName));
370             tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanMacTableOperationalDataPath(elanName));
371             tx.delete(LogicalDatastoreType.OPERATIONAL,
372                     ElanUtils.getElanInfoEntriesOperationalDataPath(elanInfo.getElanTag()));
373         } else {
374             Elan updateElanState = new ElanBuilder().setElanInterfaces(elanInterfaces).setName(elanName)
375                     .withKey(new ElanKey(elanName)).build();
376             tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanName),
377                     updateElanState);
378         }
379         return elanState;
380     }
381
382     private void deleteElanInterfaceFromConfigDS(String interfaceName, WriteTransaction tx) {
383         // removing the ElanInterface from the config data_store if interface is
384         // not present in Interface config DS
385         if (interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName) == null
386                 && elanInterfaceCache.get(interfaceName).isPresent()) {
387             tx.delete(LogicalDatastoreType.CONFIGURATION,
388                     ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName));
389         }
390     }
391
392     List<ListenableFuture<Void>> removeEntriesForElanInterface(ElanInstance elanInfo, InterfaceInfo
393             interfaceInfo, String interfaceName, boolean isLastElanInterface) {
394         String elanName = elanInfo.getElanInstanceName();
395         List<ListenableFuture<Void>> futures = new ArrayList<>();
396         futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(flowTx -> {
397             futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(interfaceTx -> {
398                 InstanceIdentifier<ElanInterfaceMac> elanInterfaceId = ElanUtils
399                         .getElanInterfaceMacEntriesOperationalDataPath(interfaceName);
400                 Optional<ElanInterfaceMac> existingElanInterfaceMac =
401                         interfaceTx.read(LogicalDatastoreType.OPERATIONAL, elanInterfaceId).checkedGet();
402                 LOG.debug("Removing the Interface:{} from elan:{}", interfaceName, elanName);
403                 if (interfaceInfo != null) {
404                     if (existingElanInterfaceMac.isPresent()) {
405                         List<MacEntry> existingMacEntries = existingElanInterfaceMac.get().getMacEntry();
406                         if (existingMacEntries != null) {
407                             List<PhysAddress> macAddresses = new ArrayList<>();
408                             for (MacEntry macEntry : existingMacEntries) {
409                                 PhysAddress macAddress = macEntry.getMacAddress();
410                                 LOG.debug("removing the  mac-entry:{} present on elanInterface:{}",
411                                         macAddress.getValue(), interfaceName);
412                                 Optional<MacEntry> macEntryOptional =
413                                         elanUtils.getMacEntryForElanInstance(interfaceTx, elanName, macAddress);
414                                 if (!isLastElanInterface && macEntryOptional.isPresent()) {
415                                     interfaceTx.delete(LogicalDatastoreType.OPERATIONAL,
416                                             ElanUtils.getMacEntryOperationalDataPath(elanName, macAddress));
417                                 }
418                                 elanUtils.deleteMacFlows(elanInfo, interfaceInfo, macEntry, flowTx);
419                                 macAddresses.add(macAddress);
420                             }
421
422                             // Removing all those MACs from External Devices belonging
423                             // to this ELAN
424                             if (isVxlanNetworkOrVxlanSegment(elanInfo) && ! macAddresses.isEmpty()) {
425                                 elanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo, macAddresses);
426                             }
427                         }
428                     }
429                     removeDefaultTermFlow(interfaceInfo.getDpId(), interfaceInfo.getInterfaceTag());
430                     removeFilterEqualsTable(elanInfo, interfaceInfo, flowTx);
431                 } else if (existingElanInterfaceMac.isPresent()) {
432                     // Interface does not exist in ConfigDS, so lets remove everything
433                     // about that interface related to Elan
434                     List<MacEntry> macEntries = existingElanInterfaceMac.get().getMacEntry();
435                     if (macEntries != null) {
436                         for (MacEntry macEntry : macEntries) {
437                             PhysAddress macAddress = macEntry.getMacAddress();
438                             if (elanUtils.getMacEntryForElanInstance(elanName, macAddress).isPresent()) {
439                                 interfaceTx.delete(LogicalDatastoreType.OPERATIONAL,
440                                         ElanUtils.getMacEntryOperationalDataPath(elanName, macAddress));
441                             }
442                         }
443                     }
444                 }
445                 if (existingElanInterfaceMac.isPresent()) {
446                     interfaceTx.delete(LogicalDatastoreType.OPERATIONAL, elanInterfaceId);
447                 }
448                 unbindService(interfaceName, interfaceTx);
449                 deleteElanInterfaceFromConfigDS(interfaceName, interfaceTx);
450             }));
451         }));
452         return futures;
453     }
454
455     private DpnInterfaces removeElanDpnInterfaceFromOperationalDataStore(String elanName, BigInteger dpId,
456                                                                          String interfaceName, long elanTag,
457                                                                          WriteTransaction tx) {
458         synchronized (elanName.intern()) {
459
460             DpnInterfaces dpnInterfaces = elanUtils.getElanInterfaceInfoByElanDpn(elanName, dpId);
461             if (dpnInterfaces != null) {
462                 List<String> interfaceLists = dpnInterfaces.getInterfaces();
463                 if (interfaceLists != null) {
464                     interfaceLists.remove(interfaceName);
465                 }
466
467                 if (interfaceLists == null || interfaceLists.isEmpty()) {
468                     deleteAllRemoteMacsInADpn(elanName, dpId, elanTag);
469                     deleteElanDpnInterface(elanName, dpId, tx);
470                 } else {
471                     dpnInterfaces = updateElanDpnInterfacesList(elanName, dpId, interfaceLists, tx);
472                 }
473             }
474             return dpnInterfaces;
475         }
476     }
477
478     private void deleteAllRemoteMacsInADpn(String elanName, BigInteger dpId, long elanTag) {
479         List<DpnInterfaces> dpnInterfaces = elanUtils.getInvolvedDpnsInElan(elanName);
480         for (DpnInterfaces dpnInterface : dpnInterfaces) {
481             BigInteger currentDpId = dpnInterface.getDpId();
482             if (!currentDpId.equals(dpId)) {
483                 for (String elanInterface : dpnInterface.getInterfaces()) {
484                     ElanInterfaceMac macs = elanUtils.getElanInterfaceMacByInterfaceName(elanInterface);
485                     if (macs == null || macs.getMacEntry() == null) {
486                         continue;
487                     }
488                     for (MacEntry mac : macs.getMacEntry()) {
489                         removeTheMacFlowInTheDPN(dpId, elanTag, currentDpId, mac);
490                         removeEtreeMacFlowInTheDPN(dpId, elanTag, currentDpId, mac);
491                     }
492                 }
493             }
494         }
495     }
496
497     private void removeEtreeMacFlowInTheDPN(BigInteger dpId, long elanTag, BigInteger currentDpId, MacEntry mac) {
498         EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
499         if (etreeLeafTag != null) {
500             removeTheMacFlowInTheDPN(dpId, etreeLeafTag.getEtreeLeafTag().getValue(), currentDpId, mac);
501         }
502     }
503
504     private void removeTheMacFlowInTheDPN(BigInteger dpId, long elanTag, BigInteger currentDpId, MacEntry mac) {
505         mdsalManager
506                 .removeFlow(dpId,
507                         MDSALUtil.buildFlow(NwConstants.ELAN_DMAC_TABLE,
508                                 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, currentDpId,
509                                         mac.getMacAddress().getValue(), elanTag)));
510     }
511
512     /*
513     * Possible Scenarios for update
514     *   a. if orig={1,2,3,4}   and updated=null or updated={}
515         then all {1,2,3,4} should be removed
516
517         b. if orig=null or orig={}  and  updated ={1,2,3,4}
518         then all {1,2,3,4} should be added
519
520         c. if orig = {1,2,3,4} updated={2,3,4}
521         then 1 should be removed
522
523         d. basically if orig = { 1,2,3,4} and updated is {1,2,3,4,5}
524         then we should just add 5
525
526         e. if orig = {1,2,3,4} updated={2,3,4,5}
527         then 1 should be removed , 5 should be added
528     * */
529     @SuppressWarnings("checkstyle:ForbiddenMethod")
530     @Override
531     protected void update(InstanceIdentifier<ElanInterface> identifier, ElanInterface original, ElanInterface update) {
532         // updating the static-Mac Entries for the existing elanInterface
533         String elanName = update.getElanInstanceName();
534         String interfaceName = update.getName();
535
536         List<StaticMacEntries> originalStaticMacEntries = original.getStaticMacEntries();
537         List<StaticMacEntries> updatedStaticMacEntries = update.getStaticMacEntries();
538         List<StaticMacEntries> deletedEntries = ElanUtils.diffOf(originalStaticMacEntries, updatedStaticMacEntries);
539         List<StaticMacEntries> updatedEntries = ElanUtils.diffOf(updatedStaticMacEntries, originalStaticMacEntries);
540
541         deletedEntries.forEach((deletedEntry) -> removeInterfaceStaticMacEntries(elanName, interfaceName,
542                 deletedEntry.getMacAddress()));
543
544         /*if updatedStaticMacEntries is NOT NULL, which means as part of update call these entries were added.
545         * Hence add the macentries for the same.*/
546         for (StaticMacEntries staticMacEntry : updatedEntries) {
547             InstanceIdentifier<MacEntry> macEntryIdentifier = getMacEntryOperationalDataPath(elanName,
548                     staticMacEntry.getMacAddress());
549             Optional<MacEntry> existingMacEntry = ElanUtils.read(broker,
550                     LogicalDatastoreType.OPERATIONAL, macEntryIdentifier);
551             WriteTransaction tx = broker.newWriteOnlyTransaction();
552             if (existingMacEntry.isPresent()) {
553                 elanForwardingEntriesHandler.updateElanInterfaceForwardingTablesList(
554                         elanName, interfaceName, existingMacEntry.get().getInterface(), existingMacEntry.get(),
555                         tx);
556             } else {
557                 elanForwardingEntriesHandler.addElanInterfaceForwardingTableList(
558                         elanName, interfaceName, staticMacEntry, tx);
559             }
560             ListenableFutures.addErrorLogging(ElanUtils.waitForTransactionToComplete(tx), LOG,
561                     "Error in update: identifier={}, original={}, update={}", identifier, original, update);
562         }
563     }
564
565     @Override
566     protected void add(InstanceIdentifier<ElanInterface> identifier, ElanInterface elanInterfaceAdded) {
567         ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
568             String elanInstanceName = elanInterfaceAdded.getElanInstanceName();
569             String interfaceName = elanInterfaceAdded.getName();
570             InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
571             if (interfaceInfo == null) {
572                 LOG.info("Interface {} is removed from Interface Oper DS due to port down ", interfaceName);
573                 return;
574             }
575             ElanInstance elanInstance = elanInstanceCache.get(elanInstanceName).orNull();
576
577             if (elanInstance == null) {
578                 elanInstance = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName)
579                         .setDescription(elanInterfaceAdded.getDescription()).build();
580                 // Add the ElanInstance in the Configuration data-store
581                 List<String> elanInterfaces = new ArrayList<>();
582                 elanInterfaces.add(interfaceName);
583                 elanInstance = ElanUtils.updateOperationalDataStore(idManager, elanInstance, elanInterfaces, tx);
584             }
585
586             Long elanTag = elanInstance.getElanTag();
587             // If elan tag is not updated, then put the elan interface into
588             // unprocessed entry map and entry. Let entries
589             // in this map get processed during ELAN update DCN.
590             if (elanTag == null) {
591                 ConcurrentLinkedQueue<ElanInterface> elanInterfaces = unProcessedElanInterfaces.get(elanInstanceName);
592                 if (elanInterfaces == null) {
593                     elanInterfaces = new ConcurrentLinkedQueue<>();
594                 }
595                 elanInterfaces.add(elanInterfaceAdded);
596                 unProcessedElanInterfaces.put(elanInstanceName, elanInterfaces);
597                 return;
598             }
599             InterfaceAddWorkerOnElan addWorker = new InterfaceAddWorkerOnElan(elanInstanceName, elanInterfaceAdded,
600                     interfaceInfo, elanInstance, this);
601             jobCoordinator.enqueueJob(elanInstanceName, addWorker, ElanConstants.JOB_MAX_RETRIES);
602         }), LOG, "Error procedding added ELAN interface");
603     }
604
605     List<ListenableFuture<Void>> handleunprocessedElanInterfaces(ElanInstance elanInstance) throws ElanException {
606         List<ListenableFuture<Void>> futures = new ArrayList<>();
607         Queue<ElanInterface> elanInterfaces = unProcessedElanInterfaces.get(elanInstance.getElanInstanceName());
608         if (elanInterfaces == null || elanInterfaces.isEmpty()) {
609             return futures;
610         }
611         for (ElanInterface elanInterface : elanInterfaces) {
612             String interfaceName = elanInterface.getName();
613             InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
614             futures.addAll(addElanInterface(elanInterface, interfaceInfo, elanInstance));
615         }
616         return futures;
617     }
618
619     void programRemoteDmacFlow(ElanInstance elanInstance, InterfaceInfo interfaceInfo,
620             WriteTransaction writeFlowGroupTx) throws ElanException {
621         ElanDpnInterfacesList elanDpnInterfacesList = elanUtils
622                 .getElanDpnInterfacesList(elanInstance.getElanInstanceName());
623         List<DpnInterfaces> dpnInterfaceLists = null;
624         if (elanDpnInterfacesList != null) {
625             dpnInterfaceLists = elanDpnInterfacesList.getDpnInterfaces();
626         }
627         if (dpnInterfaceLists == null) {
628             dpnInterfaceLists = new ArrayList<>();
629         }
630         for (DpnInterfaces dpnInterfaces : dpnInterfaceLists) {
631             BigInteger dstDpId = interfaceInfo.getDpId();
632             if (dpnInterfaces.getDpId().equals(dstDpId)) {
633                 continue;
634             }
635             List<String> remoteElanInterfaces = dpnInterfaces.getInterfaces();
636             for (String remoteIf : remoteElanInterfaces) {
637                 ElanInterfaceMac elanIfMac = elanUtils.getElanInterfaceMacByInterfaceName(remoteIf);
638                 InterfaceInfo remoteInterface = interfaceManager.getInterfaceInfo(remoteIf);
639                 if (elanIfMac == null || remoteInterface == null) {
640                     continue;
641                 }
642                 List<MacEntry> remoteMacEntries = elanIfMac.getMacEntry();
643                 if (remoteMacEntries != null) {
644                     for (MacEntry macEntry : remoteMacEntries) {
645                         String macAddress = macEntry.getMacAddress().getValue();
646                         LOG.info("Programming remote dmac {} on the newly added DPN {} for elan {}", macAddress,
647                                 dstDpId, elanInstance.getElanInstanceName());
648                         elanUtils.setupRemoteDmacFlow(dstDpId, remoteInterface.getDpId(),
649                                 remoteInterface.getInterfaceTag(), elanInstance.getElanTag(), macAddress,
650                                 elanInstance.getElanInstanceName(), writeFlowGroupTx, remoteIf, elanInstance);
651                     }
652                 }
653             }
654         }
655     }
656
657     @SuppressWarnings("checkstyle:ForbiddenMethod")
658     List<ListenableFuture<Void>> addElanInterface(ElanInterface elanInterface,
659             InterfaceInfo interfaceInfo, ElanInstance elanInstance) throws ElanException {
660         Preconditions.checkNotNull(elanInstance, "elanInstance cannot be null");
661         Preconditions.checkNotNull(interfaceInfo, "interfaceInfo cannot be null");
662         Preconditions.checkNotNull(elanInterface, "elanInterface cannot be null");
663
664         String interfaceName = elanInterface.getName();
665         String elanInstanceName = elanInterface.getElanInstanceName();
666
667         Elan elanInfo = ElanUtils.getElanByName(broker, elanInstanceName);
668         WriteTransaction tx = broker.newWriteOnlyTransaction();
669         if (elanInfo == null) {
670             List<String> elanInterfaces = new ArrayList<>();
671             elanInterfaces.add(interfaceName);
672             ElanUtils.updateOperationalDataStore(idManager, elanInstance, elanInterfaces, tx);
673         } else {
674             createElanStateList(elanInstanceName, interfaceName, tx);
675         }
676         boolean isFirstInterfaceInDpn = false;
677         // Specific actions to the DPN where the ElanInterface has been added,
678         // for example, programming the
679         // External tunnel table if needed or adding the ElanInterface to the
680         // DpnInterfaces in the operational DS.
681         BigInteger dpId = interfaceInfo.getDpId();
682         DpnInterfaces dpnInterfaces = null;
683         if (dpId != null && !dpId.equals(ElanConstants.INVALID_DPN)) {
684             synchronized (elanInstanceName.intern()) {
685                 InstanceIdentifier<DpnInterfaces> elanDpnInterfaces = ElanUtils
686                         .getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId);
687                 Optional<DpnInterfaces> existingElanDpnInterfaces = ElanUtils.read(broker,
688                         LogicalDatastoreType.OPERATIONAL, elanDpnInterfaces);
689                 if (ElanUtils.isVlan(elanInstance)) {
690                     isFirstInterfaceInDpn =  checkIfFirstInterface(interfaceName,
691                             elanInstanceName, existingElanDpnInterfaces);
692                 } else {
693                     isFirstInterfaceInDpn = !existingElanDpnInterfaces.isPresent();
694                 }
695                 if (isFirstInterfaceInDpn) {
696                     // ELAN's 1st ElanInterface added to this DPN
697                     if (!existingElanDpnInterfaces.isPresent()) {
698                         dpnInterfaces = createElanInterfacesList(elanInstanceName, interfaceName, dpId, tx);
699                     } else {
700                         List<String> elanInterfaces = existingElanDpnInterfaces.get().getInterfaces();
701                         elanInterfaces.add(interfaceName);
702                         dpnInterfaces = updateElanDpnInterfacesList(elanInstanceName, dpId,
703                                 elanInterfaces, tx);
704                     }
705                     // The 1st ElanInterface in a DPN must program the Ext Tunnel
706                     // table, but only if Elan has VNI
707                     if (isVxlanNetworkOrVxlanSegment(elanInstance)) {
708                         setExternalTunnelTable(dpId, elanInstance);
709                     }
710                     elanL2GatewayUtils.installElanL2gwDevicesLocalMacsInDpn(dpId, elanInstance, interfaceName);
711                 } else {
712                     List<String> elanInterfaces = existingElanDpnInterfaces.get().getInterfaces();
713                     elanInterfaces.add(interfaceName);
714                     if (elanInterfaces.size() == 1) { // 1st dpn interface
715                         elanL2GatewayUtils.installElanL2gwDevicesLocalMacsInDpn(dpId, elanInstance, interfaceName);
716                     }
717                     dpnInterfaces = updateElanDpnInterfacesList(elanInstanceName, dpId, elanInterfaces, tx);
718                 }
719             }
720         }
721
722         // add code to install Local/Remote BC group, unknow DMAC entry,
723         // terminating service table flow entry
724         // call bindservice of interfacemanager to create ingress table flow
725         // enty.
726         // Add interface to the ElanInterfaceForwardingEntires Container
727         createElanInterfaceTablesList(interfaceName, tx);
728         List<ListenableFuture<Void>> futures = new ArrayList<>();
729         futures.add(ElanUtils.waitForTransactionToComplete(tx));
730         installEntriesForFirstInterfaceonDpn(elanInstance, interfaceInfo, dpnInterfaces, isFirstInterfaceInDpn);
731
732         // add the vlan provider interface to remote BC group for the elan
733         // for internal vlan networks
734         if (ElanUtils.isVlan(elanInstance) && !elanInstance.isExternal()) {
735             if (interfaceManager.isExternalInterface(interfaceName)) {
736                 LOG.debug("adding vlan prv intf {} to elan {} BC group", interfaceName, elanInstanceName);
737                 handleExternalInterfaceEvent(elanInstance, dpnInterfaces, dpId);
738             }
739         }
740
741         if (isFirstInterfaceInDpn && isVxlanNetworkOrVxlanSegment(elanInstance)) {
742             //update the remote-DPNs remoteBC group entry with Tunnels
743             LOG.trace("update remote bc group for elan {} on other DPNs for newly added dpn {}", elanInstance, dpId);
744             setElanAndEtreeBCGrouponOtherDpns(elanInstance, dpId);
745         }
746
747         String jobKey = ElanUtils.getElanInterfaceJobKey(interfaceName);
748         InterfaceAddWorkerOnElanInterface addWorker = new InterfaceAddWorkerOnElanInterface(jobKey,
749                 elanInterface, interfaceInfo, elanInstance, isFirstInterfaceInDpn, this);
750         jobCoordinator.enqueueJob(jobKey, addWorker, ElanConstants.JOB_MAX_RETRIES);
751         return futures;
752     }
753
754     @SuppressWarnings("checkstyle:ForbiddenMethod")
755     List<ListenableFuture<Void>> setupEntriesForElanInterface(ElanInstance elanInstance,
756             ElanInterface elanInterface, InterfaceInfo interfaceInfo, boolean isFirstInterfaceInDpn)
757             throws ElanException {
758         String elanInstanceName = elanInstance.getElanInstanceName();
759         String interfaceName = elanInterface.getName();
760         WriteTransaction tx = broker.newWriteOnlyTransaction();
761         BigInteger dpId = interfaceInfo.getDpId();
762         WriteTransaction writeFlowGroupTx = broker.newWriteOnlyTransaction();
763         installEntriesForElanInterface(elanInstance, elanInterface, interfaceInfo,
764                 isFirstInterfaceInDpn, tx, writeFlowGroupTx);
765
766         List<StaticMacEntries> staticMacEntriesList = elanInterface.getStaticMacEntries();
767         List<PhysAddress> staticMacAddresses = Lists.newArrayList();
768
769         boolean isInterfaceOperational = isOperational(interfaceInfo);
770         if (ElanUtils.isNotEmpty(staticMacEntriesList)) {
771             for (StaticMacEntries staticMacEntry : staticMacEntriesList) {
772                 InstanceIdentifier<MacEntry> macId = getMacEntryOperationalDataPath(elanInstanceName,
773                         staticMacEntry.getMacAddress());
774                 Optional<MacEntry> existingMacEntry = ElanUtils.read(broker,
775                         LogicalDatastoreType.OPERATIONAL, macId);
776                 if (existingMacEntry.isPresent()) {
777                     elanForwardingEntriesHandler.updateElanInterfaceForwardingTablesList(
778                             elanInstanceName, interfaceName, existingMacEntry.get().getInterface(),
779                             existingMacEntry.get(), tx);
780                 } else {
781                     elanForwardingEntriesHandler
782                             .addElanInterfaceForwardingTableList(elanInstanceName, interfaceName, staticMacEntry, tx);
783                 }
784
785                 if (isInterfaceOperational) {
786                     // Setting SMAC, DMAC, UDMAC in this DPN and also in other
787                     // DPNs
788                     String macAddress = staticMacEntry.getMacAddress().getValue();
789                     LOG.info("programming smac and dmacs for {} on source and other DPNs for elan {} and interface {}",
790                             macAddress, elanInstanceName, interfaceName);
791                     elanUtils.setupMacFlows(elanInstance, interfaceInfo, ElanConstants.STATIC_MAC_TIMEOUT,
792                             staticMacEntry.getMacAddress().getValue(), true, writeFlowGroupTx);
793                 }
794             }
795
796             if (isInterfaceOperational) {
797                 // Add MAC in TOR's remote MACs via OVSDB. Outside of the loop
798                 // on purpose.
799                 for (StaticMacEntries staticMacEntry : staticMacEntriesList) {
800                     staticMacAddresses.add(staticMacEntry.getMacAddress());
801                 }
802                 elanL2GatewayUtils.scheduleAddDpnMacInExtDevices(elanInstance.getElanInstanceName(), dpId,
803                         staticMacAddresses);
804             }
805         }
806         List<ListenableFuture<Void>> futures = new ArrayList<>();
807         futures.add(ElanUtils.waitForTransactionToComplete(tx));
808         futures.add(ElanUtils.waitForTransactionToComplete(writeFlowGroupTx));
809         if (isInterfaceOperational && !interfaceManager.isExternalInterface(interfaceName)) {
810             //At this point, the interface is operational and D/SMAC flows have been configured, mark the port active
811             try {
812                 Port neutronPort = neutronVpnManager.getNeutronPort(interfaceName);
813                 if (neutronPort != null) {
814                     NeutronUtils.updatePortStatus(interfaceName, NeutronUtils.PORT_STATUS_ACTIVE, broker);
815                 }
816             } catch (IllegalArgumentException ex) {
817                 LOG.trace("Interface: {} is not part of Neutron Network", interfaceName);
818             }
819         }
820         return futures;
821     }
822
823     protected void removeInterfaceStaticMacEntries(String elanInstanceName, String interfaceName,
824             PhysAddress physAddress) {
825         InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
826         InstanceIdentifier<MacEntry> macId = getMacEntryOperationalDataPath(elanInstanceName, physAddress);
827         Optional<MacEntry> existingMacEntry = ElanUtils.read(broker,
828                 LogicalDatastoreType.OPERATIONAL, macId);
829
830         if (!existingMacEntry.isPresent()) {
831             return;
832         }
833
834         MacEntry macEntry = new MacEntryBuilder().setMacAddress(physAddress).setInterface(interfaceName)
835                 .withKey(new MacEntryKey(physAddress)).build();
836         elanForwardingEntriesHandler.deleteElanInterfaceForwardingEntries(
837                 elanInstanceCache.get(elanInstanceName).orNull(), interfaceInfo, macEntry);
838     }
839
840     private boolean checkIfFirstInterface(String elanInterface, String elanInstanceName,
841             Optional<DpnInterfaces> existingElanDpnInterfaces) {
842         String routerPortUuid = ElanUtils.getRouterPordIdFromElanInstance(broker, elanInstanceName);
843         if (!existingElanDpnInterfaces.isPresent()) {
844             return true;
845         }
846         if (elanInterface.equals(elanInstanceName) || elanInterface.equals(routerPortUuid)) {
847             return false;
848         }
849         DpnInterfaces dpnInterfaces = existingElanDpnInterfaces.get();
850         int dummyInterfaceCount =  0;
851         if (dpnInterfaces.getInterfaces().contains(routerPortUuid)) {
852             dummyInterfaceCount++;
853         }
854         if (dpnInterfaces.getInterfaces().contains(elanInstanceName)) {
855             dummyInterfaceCount++;
856         }
857         if (dpnInterfaces.getInterfaces().size() - dummyInterfaceCount == 0) {
858             return true;
859         }
860         return false;
861     }
862
863     private InstanceIdentifier<MacEntry> getMacEntryOperationalDataPath(String elanName, PhysAddress physAddress) {
864         return InstanceIdentifier.builder(ElanForwardingTables.class).child(MacTable.class, new MacTableKey(elanName))
865                 .child(MacEntry.class, new MacEntryKey(physAddress)).build();
866     }
867
868     private void installEntriesForElanInterface(ElanInstance elanInstance, ElanInterface elanInterface,
869             InterfaceInfo interfaceInfo, boolean isFirstInterfaceInDpn, WriteTransaction tx,
870             WriteTransaction writeFlowGroupTx) throws ElanException {
871         if (!isOperational(interfaceInfo)) {
872             return;
873         }
874         BigInteger dpId = interfaceInfo.getDpId();
875         if (!elanUtils.isOpenstackVniSemanticsEnforced()) {
876             elanUtils.setupTermDmacFlows(interfaceInfo, mdsalManager, writeFlowGroupTx);
877         }
878         setupFilterEqualsTable(elanInstance, interfaceInfo, writeFlowGroupTx);
879         if (isFirstInterfaceInDpn) {
880             // Terminating Service , UnknownDMAC Table.
881             // The 1st ELAN Interface in a DPN must program the INTERNAL_TUNNEL_TABLE, but only if the network type
882             // for ELAN Instance is VxLAN
883             if (isVxlanNetworkOrVxlanSegment(elanInstance)) {
884                 setupTerminateServiceTable(elanInstance, dpId, writeFlowGroupTx);
885             }
886             setupUnknownDMacTable(elanInstance, dpId, writeFlowGroupTx);
887             /*
888              * Install remote DMAC flow. This is required since this DPN is
889              * added later to the elan instance and remote DMACs of other
890              * interfaces in this elan instance are not present in the current
891              * dpn.
892              */
893             if (!interfaceManager.isExternalInterface(interfaceInfo.getInterfaceName())) {
894                 LOG.info("Programming remote dmac flows on the newly connected dpn {} for elan {} ", dpId,
895                         elanInstance.getElanInstanceName());
896                 programRemoteDmacFlow(elanInstance, interfaceInfo, writeFlowGroupTx);
897             }
898         }
899         // bind the Elan service to the Interface
900         bindService(elanInstance, elanInterface, interfaceInfo.getInterfaceTag(), tx);
901     }
902
903     public void installEntriesForFirstInterfaceonDpn(ElanInstance elanInfo, InterfaceInfo interfaceInfo,
904             DpnInterfaces dpnInterfaces, boolean isFirstInterfaceInDpn) {
905         if (!isOperational(interfaceInfo)) {
906             return;
907         }
908         // LocalBroadcast Group creation with elan-Interfaces
909         setupLocalBroadcastGroups(elanInfo, dpnInterfaces, interfaceInfo);
910         if (isFirstInterfaceInDpn) {
911             LOG.trace("waitTimeForSyncInstall is {}", WAIT_TIME_FOR_SYNC_INSTALL);
912             BigInteger dpId = interfaceInfo.getDpId();
913             // RemoteBroadcast Group creation
914             try {
915                 Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL);
916             } catch (InterruptedException e1) {
917                 LOG.warn("Error while waiting for local BC group for ELAN {} to install", elanInfo);
918             }
919             elanL2GatewayMulticastUtils.setupElanBroadcastGroups(elanInfo, dpnInterfaces, dpId);
920             try {
921                 Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL);
922             } catch (InterruptedException e1) {
923                 LOG.warn("Error while waiting for local BC group for ELAN {} to install", elanInfo);
924             }
925         }
926     }
927
928     public void setupFilterEqualsTable(ElanInstance elanInfo, InterfaceInfo interfaceInfo,
929             WriteTransaction writeFlowGroupTx) {
930         int ifTag = interfaceInfo.getInterfaceTag();
931         Flow flow = MDSALUtil.buildFlowNew(NwConstants.ELAN_FILTER_EQUALS_TABLE,
932                 getFlowRef(NwConstants.ELAN_FILTER_EQUALS_TABLE, ifTag, "group"), 9, elanInfo.getElanInstanceName(), 0,
933                 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)),
934                 ElanUtils.getTunnelIdMatchForFilterEqualsLPortTag(ifTag),
935                 elanUtils.getInstructionsInPortForOutGroup(interfaceInfo.getInterfaceName()));
936
937         mdsalManager.addFlowToTx(interfaceInfo.getDpId(), flow, writeFlowGroupTx);
938
939         Flow flowEntry = MDSALUtil.buildFlowNew(NwConstants.ELAN_FILTER_EQUALS_TABLE,
940                 getFlowRef(NwConstants.ELAN_FILTER_EQUALS_TABLE, ifTag, "drop"), 10, elanInfo.getElanInstanceName(), 0,
941                 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)),
942                 getMatchesForFilterEqualsLPortTag(ifTag), MDSALUtil.buildInstructionsDrop());
943
944         mdsalManager.addFlowToTx(interfaceInfo.getDpId(), flowEntry, writeFlowGroupTx);
945     }
946
947     public void removeFilterEqualsTable(ElanInstance elanInfo, InterfaceInfo interfaceInfo,
948             WriteTransaction deleteFlowGroupTx) {
949         int ifTag = interfaceInfo.getInterfaceTag();
950         Flow flow = MDSALUtil.buildFlow(NwConstants.ELAN_FILTER_EQUALS_TABLE,
951                 getFlowRef(NwConstants.ELAN_FILTER_EQUALS_TABLE, ifTag, "group"));
952
953         mdsalManager.removeFlowToTx(interfaceInfo.getDpId(), flow, deleteFlowGroupTx);
954
955         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.ELAN_FILTER_EQUALS_TABLE,
956                 getFlowRef(NwConstants.ELAN_FILTER_EQUALS_TABLE, ifTag, "drop"), 10, elanInfo.getElanInstanceName(), 0,
957                 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)),
958                 getMatchesForFilterEqualsLPortTag(ifTag), MDSALUtil.buildInstructionsDrop());
959
960         mdsalManager.removeFlowToTx(interfaceInfo.getDpId(), flowEntity, deleteFlowGroupTx);
961     }
962
963     private List<Bucket> getRemoteBCGroupBucketInfos(ElanInstance elanInfo, int bucketKeyStart,
964                                                      InterfaceInfo interfaceInfo, long elanTag) {
965         return elanL2GatewayMulticastUtils.getRemoteBCGroupBuckets(elanInfo, null, interfaceInfo.getDpId(),
966                 bucketKeyStart, elanTag);
967     }
968
969     private void setElanAndEtreeBCGrouponOtherDpns(ElanInstance elanInfo, BigInteger dpId) {
970         int elanTag = elanInfo.getElanTag().intValue();
971         long groupId = ElanUtils.getElanRemoteBCGId(elanTag);
972         setBCGrouponOtherDpns(elanInfo, dpId, elanTag, groupId);
973         EtreeInstance etreeInstance = elanInfo.augmentation(EtreeInstance.class);
974         if (etreeInstance != null) {
975             int etreeLeafTag = etreeInstance.getEtreeLeafTagVal().getValue().intValue();
976             long etreeLeafGroupId = ElanUtils.getEtreeLeafRemoteBCGId(etreeLeafTag);
977             setBCGrouponOtherDpns(elanInfo, dpId, etreeLeafTag, etreeLeafGroupId);
978         }
979     }
980
981     @SuppressWarnings("checkstyle:IllegalCatch")
982     private void setBCGrouponOtherDpns(ElanInstance elanInfo, BigInteger dpId, int elanTag, long groupId) {
983         int bucketId = 0;
984         ElanDpnInterfacesList elanDpns = elanUtils.getElanDpnInterfacesList(elanInfo.getElanInstanceName());
985         if (elanDpns != null) {
986             List<DpnInterfaces> dpnInterfaces = elanDpns.getDpnInterfaces();
987             for (DpnInterfaces dpnInterface : dpnInterfaces) {
988                 List<Bucket> remoteListBucketInfo = new ArrayList<>();
989                 if (elanUtils.isDpnPresent(dpnInterface.getDpId()) && !Objects.equals(dpnInterface.getDpId(), dpId)
990                         && dpnInterface.getInterfaces() != null && !dpnInterface.getInterfaces().isEmpty()) {
991                     List<Action> listAction = new ArrayList<>();
992                     int actionKey = 0;
993                     listAction.add(new ActionGroup(ElanUtils.getElanLocalBCGId(elanTag)).buildAction(++actionKey));
994                     remoteListBucketInfo.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId,
995                             MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
996                     bucketId++;
997                     for (DpnInterfaces otherFes : dpnInterfaces) {
998                         if (elanUtils.isDpnPresent(otherFes.getDpId()) && !Objects.equals(otherFes.getDpId(),
999                             dpnInterface.getDpId()) && otherFes.getInterfaces() != null
1000                             && !otherFes.getInterfaces().isEmpty()) {
1001                             try {
1002                                 List<Action> remoteListActionInfo = elanItmUtils.getInternalTunnelItmEgressAction(
1003                                         dpnInterface.getDpId(), otherFes.getDpId(),
1004                                         elanUtils.isOpenstackVniSemanticsEnforced()
1005                                                 ? elanUtils.getVxlanSegmentationId(elanInfo) : elanTag);
1006                                 if (!remoteListActionInfo.isEmpty()) {
1007                                     remoteListBucketInfo.add(MDSALUtil.buildBucket(remoteListActionInfo, MDSALUtil
1008                                             .GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
1009                                     bucketId++;
1010                                 }
1011                             } catch (Exception ex) {
1012                                 LOG.error("setElanBCGrouponOtherDpns failed due to Exception caught; "
1013                                         + "Logical Group Interface not found between source Dpn - {}, "
1014                                         + "destination Dpn - {} ", dpnInterface.getDpId(), otherFes.getDpId(), ex);
1015                                 return;
1016                             }
1017                         }
1018                     }
1019                     List<Bucket> elanL2GwDevicesBuckets = elanL2GatewayMulticastUtils
1020                             .getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnInterface.getDpId(), bucketId);
1021                     remoteListBucketInfo.addAll(elanL2GwDevicesBuckets);
1022
1023                     if (remoteListBucketInfo.isEmpty()) {
1024                         LOG.debug("No ITM is present on Dpn - {} ", dpnInterface.getDpId());
1025                         continue;
1026                     }
1027                     Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
1028                             MDSALUtil.buildBucketLists(remoteListBucketInfo));
1029                     LOG.trace("Installing remote bc group {} on dpnId {}", group, dpnInterface.getDpId());
1030                     mdsalManager.syncInstallGroup(dpnInterface.getDpId(), group);
1031                 }
1032             }
1033             try {
1034                 Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL);
1035             } catch (InterruptedException e1) {
1036                 LOG.warn("Error while waiting for remote BC group on other DPNs for ELAN {} to install", elanInfo);
1037             }
1038         }
1039     }
1040
1041     /**
1042      * Returns the bucket info with the given interface as the only bucket.
1043      */
1044     private Bucket getLocalBCGroupBucketInfo(InterfaceInfo interfaceInfo, int bucketIdStart) {
1045         return MDSALUtil.buildBucket(getInterfacePortActions(interfaceInfo), MDSALUtil.GROUP_WEIGHT, bucketIdStart,
1046                 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
1047     }
1048
1049     private List<MatchInfo> buildMatchesForVni(Long vni) {
1050         List<MatchInfo> mkMatches = new ArrayList<>();
1051         MatchInfo match = new MatchTunnelId(BigInteger.valueOf(vni));
1052         mkMatches.add(match);
1053         return mkMatches;
1054     }
1055
1056     private List<InstructionInfo> getInstructionsForOutGroup(long groupId) {
1057         List<InstructionInfo> mkInstructions = new ArrayList<>();
1058         mkInstructions.add(new InstructionWriteActions(Collections.singletonList(new ActionGroup(groupId))));
1059         return mkInstructions;
1060     }
1061
1062     private List<MatchInfo> getMatchesForElanTag(long elanTag, boolean isSHFlagSet) {
1063         List<MatchInfo> mkMatches = new ArrayList<>();
1064         // Matching metadata
1065         mkMatches.add(new MatchMetadata(
1066                 ElanUtils.getElanMetadataLabel(elanTag, isSHFlagSet), MetaDataUtil.METADATA_MASK_SERVICE_SH_FLAG));
1067         return mkMatches;
1068     }
1069
1070     /**
1071      * Builds the list of instructions to be installed in the INTERNAL_TUNNEL_TABLE (36) / EXTERNAL_TUNNEL_TABLE (38)
1072      * which so far consists of writing the elanTag in metadata and send the packet to ELAN_DMAC_TABLE.
1073      *
1074      * @param elanTag
1075      *            elanTag to be written in metadata when flow is selected
1076      * @return the instructions ready to be installed in a flow
1077      */
1078     private List<InstructionInfo> getInstructionsIntOrExtTunnelTable(Long elanTag) {
1079         List<InstructionInfo> mkInstructions = new ArrayList<>();
1080         mkInstructions.add(new InstructionWriteMetadata(ElanHelper.getElanMetadataLabel(elanTag), ElanHelper
1081                 .getElanMetadataMask()));
1082         /* applicable for EXTERNAL_TUNNEL_TABLE only
1083         * TODO: We should point to SMAC or DMAC depending on a configuration property to enable mac learning
1084         */
1085         mkInstructions.add(new InstructionGotoTable(NwConstants.ELAN_DMAC_TABLE));
1086         return mkInstructions;
1087     }
1088
1089     // Install DMAC entry on dst DPN
1090     @SuppressWarnings("checkstyle:ForbiddenMethod")
1091     public List<ListenableFuture<Void>> installDMacAddressTables(ElanInstance elanInfo, InterfaceInfo interfaceInfo,
1092             BigInteger dstDpId) throws ElanException {
1093         String interfaceName = interfaceInfo.getInterfaceName();
1094         ElanInterfaceMac elanInterfaceMac = elanUtils.getElanInterfaceMacByInterfaceName(interfaceName);
1095         if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) {
1096             List<MacEntry> macEntries = elanInterfaceMac.getMacEntry();
1097             WriteTransaction writeFlowTx = broker.newWriteOnlyTransaction();
1098             for (MacEntry macEntry : macEntries) {
1099                 String macAddress = macEntry.getMacAddress().getValue();
1100                 LOG.info("Installing remote dmac for mac address {} and interface {}", macAddress, interfaceName);
1101                 synchronized (ElanUtils.getElanMacDPNKey(elanInfo.getElanTag(), macAddress,
1102                         interfaceInfo.getDpId())) {
1103                     LOG.info("Acquired lock for mac : {}, proceeding with remote dmac install operation", macAddress);
1104                     elanUtils.setupDMacFlowOnRemoteDpn(elanInfo, interfaceInfo, dstDpId, macAddress,
1105                             writeFlowTx);
1106                 }
1107             }
1108             return Collections.singletonList(ElanUtils.waitForTransactionToComplete(writeFlowTx));
1109         }
1110         return Collections.emptyList();
1111     }
1112
1113     private void createDropBucket(List<Bucket> listBucket) {
1114         List<Action> actionsInfos = new ArrayList<>();
1115         actionsInfos.add(new ActionDrop().buildAction());
1116         Bucket dropBucket = MDSALUtil.buildBucket(actionsInfos, MDSALUtil.GROUP_WEIGHT, 0, MDSALUtil.WATCH_PORT,
1117                 MDSALUtil.WATCH_GROUP);
1118         listBucket.add(dropBucket);
1119     }
1120
1121     public void setupLocalBroadcastGroups(ElanInstance elanInfo, DpnInterfaces newDpnInterface,
1122             InterfaceInfo interfaceInfo) {
1123         setupStandardLocalBroadcastGroups(elanInfo, newDpnInterface, interfaceInfo);
1124         setupLeavesLocalBroadcastGroups(elanInfo, newDpnInterface, interfaceInfo);
1125     }
1126
1127     public void setupStandardLocalBroadcastGroups(ElanInstance elanInfo, DpnInterfaces newDpnInterface,
1128             InterfaceInfo interfaceInfo) {
1129         List<Bucket> listBucket = new ArrayList<>();
1130         int bucketId = 0;
1131         long groupId = ElanUtils.getElanLocalBCGId(elanInfo.getElanTag());
1132
1133         List<String> interfaces = new ArrayList<>();
1134         if (newDpnInterface != null) {
1135             interfaces = newDpnInterface.getInterfaces();
1136         }
1137         for (String ifName : interfaces) {
1138             // In case if there is a InterfacePort in the cache which is not in
1139             // operational state, skip processing it
1140             InterfaceInfo ifInfo = interfaceManager
1141                     .getInterfaceInfoFromOperationalDataStore(ifName, interfaceInfo.getInterfaceType());
1142             if (!isOperational(ifInfo)) {
1143                 continue;
1144             }
1145
1146             if (!interfaceManager.isExternalInterface(ifName)) {
1147                 listBucket.add(MDSALUtil.buildBucket(getInterfacePortActions(ifInfo), MDSALUtil.GROUP_WEIGHT, bucketId,
1148                         MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
1149                 bucketId++;
1150             }
1151         }
1152
1153         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
1154                 MDSALUtil.buildBucketLists(listBucket));
1155         LOG.trace("installing the localBroadCast Group:{}", group);
1156         mdsalManager.syncInstallGroup(interfaceInfo.getDpId(), group);
1157     }
1158
1159     private void setupLeavesLocalBroadcastGroups(ElanInstance elanInfo, DpnInterfaces newDpnInterface,
1160             InterfaceInfo interfaceInfo) {
1161         EtreeInstance etreeInstance = elanInfo.augmentation(EtreeInstance.class);
1162         if (etreeInstance != null) {
1163             List<Bucket> listBucket = new ArrayList<>();
1164             int bucketId = 0;
1165
1166             List<String> interfaces = new ArrayList<>();
1167             if (newDpnInterface != null) {
1168                 interfaces = newDpnInterface.getInterfaces();
1169             }
1170             for (String ifName : interfaces) {
1171                 // In case if there is a InterfacePort in the cache which is not
1172                 // in
1173                 // operational state, skip processing it
1174                 InterfaceInfo ifInfo = interfaceManager
1175                         .getInterfaceInfoFromOperationalDataStore(ifName, interfaceInfo.getInterfaceType());
1176                 if (!isOperational(ifInfo)) {
1177                     continue;
1178                 }
1179
1180                 if (!interfaceManager.isExternalInterface(ifName)) {
1181                     // only add root interfaces
1182                     bucketId = addInterfaceIfRootInterface(bucketId, ifName, listBucket, ifInfo);
1183                 }
1184             }
1185
1186             if (listBucket.isEmpty()) { // No Buckets
1187                 createDropBucket(listBucket);
1188             }
1189
1190             long etreeLeafTag = etreeInstance.getEtreeLeafTagVal().getValue();
1191             long groupId = ElanUtils.getEtreeLeafLocalBCGId(etreeLeafTag);
1192             Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
1193                     MDSALUtil.buildBucketLists(listBucket));
1194             LOG.trace("installing the localBroadCast Group:{}", group);
1195             mdsalManager.syncInstallGroup(interfaceInfo.getDpId(), group);
1196         }
1197     }
1198
1199     private int addInterfaceIfRootInterface(int bucketId, String ifName, List<Bucket> listBucket,
1200             InterfaceInfo ifInfo) {
1201         Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(ifName);
1202         if (etreeInterface.isPresent() && etreeInterface.get().getEtreeInterfaceType() == EtreeInterfaceType.Root) {
1203             listBucket.add(MDSALUtil.buildBucket(getInterfacePortActions(ifInfo), MDSALUtil.GROUP_WEIGHT, bucketId,
1204                     MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
1205             bucketId++;
1206         }
1207         return bucketId;
1208     }
1209
1210     public void removeLocalBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo,
1211             WriteTransaction deleteFlowGroupTx) {
1212         BigInteger dpnId = interfaceInfo.getDpId();
1213         long groupId = ElanUtils.getElanLocalBCGId(elanInfo.getElanTag());
1214         List<Bucket> listBuckets = new ArrayList<>();
1215         int bucketId = 0;
1216         listBuckets.add(getLocalBCGroupBucketInfo(interfaceInfo, bucketId));
1217         // listBuckets.addAll(getRemoteBCGroupBucketInfos(elanInfo, 1,
1218         // interfaceInfo));
1219         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
1220                 MDSALUtil.buildBucketLists(listBuckets));
1221         LOG.trace("deleted the localBroadCast Group:{}", group);
1222         mdsalManager.removeGroupToTx(dpnId, group, deleteFlowGroupTx);
1223     }
1224
1225     public void removeElanBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo,
1226             WriteTransaction deleteFlowGroupTx) {
1227         int bucketId = 0;
1228         int actionKey = 0;
1229         Long elanTag = elanInfo.getElanTag();
1230         List<Bucket> listBuckets = new ArrayList<>();
1231         List<Action> listAction = new ArrayList<>();
1232         listAction.add(new ActionGroup(++actionKey, ElanUtils.getElanLocalBCGId(elanTag)).buildAction());
1233         listBuckets.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT,
1234                 MDSALUtil.WATCH_GROUP));
1235         bucketId++;
1236         listBuckets.addAll(getRemoteBCGroupBucketInfos(elanInfo, bucketId, interfaceInfo, elanTag));
1237         BigInteger dpnId = interfaceInfo.getDpId();
1238         long groupId = ElanUtils.getElanRemoteBCGId(elanInfo.getElanTag());
1239         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
1240                 MDSALUtil.buildBucketLists(listBuckets));
1241         LOG.trace("deleting the remoteBroadCast group:{}", group);
1242         mdsalManager.removeGroupToTx(dpnId, group, deleteFlowGroupTx);
1243     }
1244
1245     /**
1246      * Installs a flow in the External Tunnel table consisting in translating
1247      * the VNI retrieved from the packet that came over a tunnel with a TOR into
1248      * elanTag that will be used later in the ELANs pipeline.
1249      *
1250      * @param dpnId
1251      *            the dpn id
1252      * @param elanInfo
1253      *            the elan info
1254      */
1255     public void setExternalTunnelTable(BigInteger dpnId, ElanInstance elanInfo) {
1256         long elanTag = elanInfo.getElanTag();
1257         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.EXTERNAL_TUNNEL_TABLE,
1258                 getFlowRef(NwConstants.EXTERNAL_TUNNEL_TABLE, elanTag), 5, // prio
1259                 elanInfo.getElanInstanceName(), // flowName
1260                 0, // idleTimeout
1261                 0, // hardTimeout
1262                 ITMConstants.COOKIE_ITM_EXTERNAL.add(BigInteger.valueOf(elanTag)),
1263                 buildMatchesForVni(ElanUtils.getVxlanSegmentationId(elanInfo)),
1264                 getInstructionsIntOrExtTunnelTable(elanTag));
1265
1266         mdsalManager.installFlow(flowEntity);
1267     }
1268
1269     /**
1270      * Removes, from External Tunnel table, the flow that translates from VNI to
1271      * elanTag. Important: ensure this method is only called whenever there is
1272      * no other ElanInterface in the specified DPN
1273      *
1274      * @param dpnId
1275      *            DPN whose Ext Tunnel table is going to be modified
1276      * @param elanInfo
1277      *            holds the elanTag needed for selecting the flow to be removed
1278      */
1279     public void unsetExternalTunnelTable(BigInteger dpnId, ElanInstance elanInfo) {
1280         // TODO: Use DataStoreJobCoordinator in order to avoid that removing the
1281         // last ElanInstance plus
1282         // adding a new one does (almost at the same time) are executed in that
1283         // exact order
1284
1285         String flowId = getFlowRef(NwConstants.EXTERNAL_TUNNEL_TABLE, elanInfo.getElanTag());
1286         FlowEntity flowEntity = new FlowEntityBuilder()
1287             .setDpnId(dpnId)
1288             .setTableId(NwConstants.EXTERNAL_TUNNEL_TABLE)
1289             .setFlowId(flowId)
1290             .build();
1291         mdsalManager.removeFlow(flowEntity);
1292     }
1293
1294     public void setupTerminateServiceTable(ElanInstance elanInfo, BigInteger dpId, WriteTransaction writeFlowGroupTx) {
1295         setupTerminateServiceTable(elanInfo, dpId, elanInfo.getElanTag(), writeFlowGroupTx);
1296         setupEtreeTerminateServiceTable(elanInfo, dpId, writeFlowGroupTx);
1297     }
1298
1299     public void setupTerminateServiceTable(ElanInstance elanInfo, BigInteger dpId, long elanTag,
1300             WriteTransaction writeFlowGroupTx) {
1301         List<? extends MatchInfoBase> listMatchInfoBase;
1302         List<InstructionInfo> instructionInfos;
1303         long serviceId;
1304         if (!elanUtils.isOpenstackVniSemanticsEnforced()) {
1305             serviceId = elanTag;
1306             listMatchInfoBase = ElanUtils.getTunnelMatchesForServiceId((int) elanTag);
1307             instructionInfos = getInstructionsForOutGroup(ElanUtils.getElanLocalBCGId(elanTag));
1308         } else {
1309             serviceId = elanUtils.getVxlanSegmentationId(elanInfo);
1310             listMatchInfoBase = buildMatchesForVni(serviceId);
1311             instructionInfos = getInstructionsIntOrExtTunnelTable(elanTag);
1312         }
1313         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INTERNAL_TUNNEL_TABLE,
1314                 getFlowRef(NwConstants.INTERNAL_TUNNEL_TABLE, serviceId), 5, String.format("%s:%d", "ITM Flow Entry ",
1315                 elanTag), 0, 0, ITMConstants.COOKIE_ITM.add(BigInteger.valueOf(elanTag)), listMatchInfoBase,
1316                 instructionInfos);
1317         mdsalManager.addFlowToTx(flowEntity, writeFlowGroupTx);
1318     }
1319
1320     private void setupEtreeTerminateServiceTable(ElanInstance elanInfo, BigInteger dpId,
1321             WriteTransaction writeFlowGroupTx) {
1322         EtreeInstance etreeInstance = elanInfo.augmentation(EtreeInstance.class);
1323         if (etreeInstance != null) {
1324             setupTerminateServiceTable(elanInfo, dpId, etreeInstance.getEtreeLeafTagVal().getValue(), writeFlowGroupTx);
1325         }
1326     }
1327
1328     public void setupUnknownDMacTable(ElanInstance elanInfo, BigInteger dpId, WriteTransaction writeFlowGroupTx) {
1329         long elanTag = elanInfo.getElanTag();
1330         installLocalUnknownFlow(elanInfo, dpId, elanTag, writeFlowGroupTx);
1331         installRemoteUnknownFlow(elanInfo, dpId, elanTag, writeFlowGroupTx);
1332         setupEtreeUnknownDMacTable(elanInfo, dpId, elanTag, writeFlowGroupTx);
1333     }
1334
1335     private void setupEtreeUnknownDMacTable(ElanInstance elanInfo, BigInteger dpId, long elanTag,
1336             WriteTransaction writeFlowGroupTx) {
1337         EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
1338         if (etreeLeafTag != null) {
1339             long leafTag = etreeLeafTag.getEtreeLeafTag().getValue();
1340             installRemoteUnknownFlow(elanInfo, dpId, leafTag, writeFlowGroupTx);
1341             installLocalUnknownFlow(elanInfo, dpId, leafTag, writeFlowGroupTx);
1342         }
1343     }
1344
1345     private void installLocalUnknownFlow(ElanInstance elanInfo, BigInteger dpId, long elanTag,
1346             WriteTransaction writeFlowGroupTx) {
1347         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.ELAN_UNKNOWN_DMAC_TABLE,
1348                 getUnknownDmacFlowRef(NwConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag,/* SH flag */false),
1349                 5, elanInfo.getElanInstanceName(), 0, 0,
1350                 ElanConstants.COOKIE_ELAN_UNKNOWN_DMAC.add(BigInteger.valueOf(elanTag)),
1351                 getMatchesForElanTag(elanTag, /* SH flag */false),
1352                 getInstructionsForOutGroup(ElanUtils.getElanRemoteBCGId(elanTag)));
1353
1354         mdsalManager.addFlowToTx(flowEntity, writeFlowGroupTx);
1355     }
1356
1357     private void installRemoteUnknownFlow(ElanInstance elanInfo, BigInteger dpId, long elanTag,
1358             WriteTransaction writeFlowGroupTx) {
1359         // only if ELAN can connect to external network, perform the following
1360
1361         if (isVxlanNetworkOrVxlanSegment(elanInfo) || ElanUtils.isVlan(elanInfo) || ElanUtils.isFlat(elanInfo)) {
1362             FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.ELAN_UNKNOWN_DMAC_TABLE,
1363                     getUnknownDmacFlowRef(NwConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag,/* SH flag */true),
1364                     5, elanInfo.getElanInstanceName(), 0, 0,
1365                     ElanConstants.COOKIE_ELAN_UNKNOWN_DMAC.add(BigInteger.valueOf(elanTag)),
1366                     getMatchesForElanTag(elanTag, /* SH flag */true),
1367                     getInstructionsForOutGroup(ElanUtils.getElanLocalBCGId(elanTag)));
1368             mdsalManager.addFlowToTx(flowEntity, writeFlowGroupTx);
1369         }
1370     }
1371
1372
1373     private void removeUnknownDmacFlow(BigInteger dpId, ElanInstance elanInfo, WriteTransaction deleteFlowGroupTx,
1374             long elanTag) {
1375         Flow flow = new FlowBuilder().setId(new FlowId(getUnknownDmacFlowRef(NwConstants.ELAN_UNKNOWN_DMAC_TABLE,
1376                 elanTag, SH_FLAG_UNSET))).setTableId(NwConstants.ELAN_UNKNOWN_DMAC_TABLE).build();
1377         mdsalManager.removeFlowToTx(dpId, flow, deleteFlowGroupTx);
1378
1379         if (isVxlanNetworkOrVxlanSegment(elanInfo)) {
1380             Flow flow2 = new FlowBuilder().setId(new FlowId(getUnknownDmacFlowRef(NwConstants.ELAN_UNKNOWN_DMAC_TABLE,
1381                     elanTag, SH_FLAG_SET))).setTableId(NwConstants.ELAN_UNKNOWN_DMAC_TABLE)
1382                     .build();
1383             mdsalManager.removeFlowToTx(dpId, flow2, deleteFlowGroupTx);
1384         }
1385     }
1386
1387     private void removeDefaultTermFlow(BigInteger dpId, long elanTag) {
1388         elanUtils.removeTerminatingServiceAction(dpId, (int) elanTag);
1389     }
1390
1391     private void bindService(ElanInstance elanInfo, ElanInterface elanInterface, int lportTag, WriteTransaction tx) {
1392         if (isStandardElanService(elanInterface)) {
1393             bindElanService(elanInfo.getElanTag(), elanInfo.getElanInstanceName(),
1394                     elanInterface.getName(), lportTag, tx);
1395         } else { // Etree service
1396             bindEtreeService(elanInfo, elanInterface, lportTag, tx);
1397         }
1398     }
1399
1400     private void bindElanService(long elanTag, String elanInstanceName, String interfaceName, int lportTag,
1401             WriteTransaction tx) {
1402         int instructionKey = 0;
1403         List<Instruction> instructions = new ArrayList<>();
1404         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(ElanHelper.getElanMetadataLabel(elanTag),
1405                 MetaDataUtil.METADATA_MASK_SERVICE, ++instructionKey));
1406
1407         List<Action> actions = new ArrayList<>();
1408         actions.add(new ActionRegLoad(0, NxmNxReg1.class, 0, ElanConstants.INTERFACE_TAG_LENGTH - 1,
1409                 lportTag).buildAction());
1410         actions.add(new ActionRegLoad(1, ElanConstants.ELAN_REG_ID, 0, ElanConstants.ELAN_TAG_LENGTH - 1,
1411                 elanTag).buildAction());
1412         instructions.add(MDSALUtil.buildApplyActionsInstruction(actions, ++instructionKey));
1413
1414         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.ARP_CHECK_TABLE,
1415                 ++instructionKey));
1416
1417         short elanServiceIndex = ServiceIndex.getIndex(NwConstants.ELAN_SERVICE_NAME, NwConstants.ELAN_SERVICE_INDEX);
1418         BoundServices serviceInfo = ElanUtils.getBoundServices(
1419                 String.format("%s.%s.%s", "elan", elanInstanceName, interfaceName), elanServiceIndex,
1420                 NwConstants.ELAN_SERVICE_INDEX, NwConstants.COOKIE_ELAN_INGRESS_TABLE, instructions);
1421         InstanceIdentifier<BoundServices> bindServiceId = ElanUtils.buildServiceId(interfaceName, elanServiceIndex);
1422         Optional<BoundServices> existingElanService = ElanUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
1423                 bindServiceId);
1424         if (!existingElanService.isPresent()) {
1425             tx.put(LogicalDatastoreType.CONFIGURATION, bindServiceId, serviceInfo,
1426                     WriteTransaction.CREATE_MISSING_PARENTS);
1427         }
1428     }
1429
1430     private void bindEtreeService(ElanInstance elanInfo, ElanInterface elanInterface, int lportTag,
1431             WriteTransaction tx) {
1432         if (elanInterface.augmentation(EtreeInterface.class).getEtreeInterfaceType() == EtreeInterfaceType.Root) {
1433             bindElanService(elanInfo.getElanTag(), elanInfo.getElanInstanceName(), elanInterface.getName(),
1434                     lportTag, tx);
1435         } else {
1436             EtreeInstance etreeInstance = elanInfo.augmentation(EtreeInstance.class);
1437             if (etreeInstance == null) {
1438                 LOG.error("EtreeInterface {} is associated with a non EtreeInstance: {}",
1439                         elanInterface.getName(), elanInfo.getElanInstanceName());
1440             } else {
1441                 bindElanService(etreeInstance.getEtreeLeafTagVal().getValue(), elanInfo.getElanInstanceName(),
1442                         elanInterface.getName(), lportTag, tx);
1443             }
1444         }
1445     }
1446
1447     private boolean isStandardElanService(ElanInterface elanInterface) {
1448         return elanInterface.augmentation(EtreeInterface.class) == null;
1449     }
1450
1451     protected void unbindService(String interfaceName, ReadWriteTransaction tx) throws ReadFailedException {
1452         short elanServiceIndex = ServiceIndex.getIndex(NwConstants.ELAN_SERVICE_NAME, NwConstants.ELAN_SERVICE_INDEX);
1453         InstanceIdentifier<BoundServices> bindServiceId = ElanUtils.buildServiceId(interfaceName, elanServiceIndex);
1454         if (tx.read(LogicalDatastoreType.CONFIGURATION, bindServiceId).checkedGet().isPresent()) {
1455             tx.delete(LogicalDatastoreType.CONFIGURATION, bindServiceId);
1456         }
1457     }
1458
1459     private String getFlowRef(long tableId, long elanTag) {
1460         return String.valueOf(tableId) + elanTag;
1461     }
1462
1463     private String getFlowRef(long tableId, long elanTag, String flowName) {
1464         return new StringBuffer().append(tableId).append(NwConstants.FLOWID_SEPARATOR).append(elanTag)
1465                 .append(NwConstants.FLOWID_SEPARATOR).append(flowName).toString();
1466     }
1467
1468     private String getUnknownDmacFlowRef(long tableId, long elanTag, boolean shFlag) {
1469         return String.valueOf(tableId) + elanTag + shFlag;
1470     }
1471
1472     private List<Action> getInterfacePortActions(InterfaceInfo interfaceInfo) {
1473         List<Action> listAction = new ArrayList<>();
1474         int actionKey = 0;
1475         listAction.add(
1476             new ActionSetFieldTunnelId(BigInteger.valueOf(interfaceInfo.getInterfaceTag())).buildAction(actionKey));
1477         actionKey++;
1478         listAction.add(new ActionNxResubmit(NwConstants.ELAN_FILTER_EQUALS_TABLE).buildAction(actionKey));
1479         return listAction;
1480     }
1481
1482     private DpnInterfaces updateElanDpnInterfacesList(String elanInstanceName, BigInteger dpId,
1483             List<String> interfaceNames, WriteTransaction tx) {
1484         DpnInterfaces dpnInterface = new DpnInterfacesBuilder().setDpId(dpId).setInterfaces(interfaceNames)
1485                 .withKey(new DpnInterfacesKey(dpId)).build();
1486         tx.put(LogicalDatastoreType.OPERATIONAL,
1487                 ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId), dpnInterface,
1488                 WriteTransaction.CREATE_MISSING_PARENTS);
1489         return dpnInterface;
1490     }
1491
1492     /**
1493      * Delete elan dpn interface from operational DS.
1494      *
1495      * @param elanInstanceName
1496      *            the elan instance name
1497      * @param dpId
1498      *            the dp id
1499      */
1500     private void deleteElanDpnInterface(String elanInstanceName, BigInteger dpId, WriteTransaction tx) {
1501         InstanceIdentifier<DpnInterfaces> dpnInterfacesId = ElanUtils
1502                 .getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId);
1503         Optional<DpnInterfaces> dpnInterfaces = ElanUtils.read(broker,
1504                 LogicalDatastoreType.OPERATIONAL, dpnInterfacesId);
1505         if (dpnInterfaces.isPresent()) {
1506             tx.delete(LogicalDatastoreType.OPERATIONAL, dpnInterfacesId);
1507         }
1508     }
1509
1510     private DpnInterfaces createElanInterfacesList(String elanInstanceName, String interfaceName, BigInteger dpId,
1511             WriteTransaction tx) {
1512         List<String> interfaceNames = new ArrayList<>();
1513         interfaceNames.add(interfaceName);
1514         DpnInterfaces dpnInterface = new DpnInterfacesBuilder().setDpId(dpId).setInterfaces(interfaceNames)
1515                 .withKey(new DpnInterfacesKey(dpId)).build();
1516         tx.put(LogicalDatastoreType.OPERATIONAL,
1517                 ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId), dpnInterface,
1518                 WriteTransaction.CREATE_MISSING_PARENTS);
1519         return dpnInterface;
1520     }
1521
1522     private void createElanInterfaceTablesList(String interfaceName, WriteTransaction tx) {
1523         InstanceIdentifier<ElanInterfaceMac> elanInterfaceMacTables = ElanUtils
1524                 .getElanInterfaceMacEntriesOperationalDataPath(interfaceName);
1525         Optional<ElanInterfaceMac> interfaceMacTables = ElanUtils.read(broker,
1526                 LogicalDatastoreType.OPERATIONAL, elanInterfaceMacTables);
1527         // Adding new Elan Interface Port to the operational DataStore without
1528         // Static-Mac Entries..
1529         if (!interfaceMacTables.isPresent()) {
1530             ElanInterfaceMac elanInterfaceMacTable = new ElanInterfaceMacBuilder().setElanInterface(interfaceName)
1531                     .withKey(new ElanInterfaceMacKey(interfaceName)).build();
1532             tx.put(LogicalDatastoreType.OPERATIONAL,
1533                     ElanUtils.getElanInterfaceMacEntriesOperationalDataPath(interfaceName), elanInterfaceMacTable,
1534                     WriteTransaction.CREATE_MISSING_PARENTS);
1535         }
1536     }
1537
1538     private void createElanStateList(String elanInstanceName, String interfaceName, WriteTransaction tx) {
1539         InstanceIdentifier<Elan> elanInstance = ElanUtils.getElanInstanceOperationalDataPath(elanInstanceName);
1540         Optional<Elan> elanInterfaceLists = ElanUtils.read(broker,
1541                 LogicalDatastoreType.OPERATIONAL, elanInstance);
1542         // Adding new Elan Interface Port to the operational DataStore without
1543         // Static-Mac Entries..
1544         if (elanInterfaceLists.isPresent()) {
1545             List<String> interfaceLists = elanInterfaceLists.get().getElanInterfaces();
1546             if (interfaceLists == null) {
1547                 interfaceLists = new ArrayList<>();
1548             }
1549             interfaceLists.add(interfaceName);
1550             Elan elanState = new ElanBuilder().setName(elanInstanceName).setElanInterfaces(interfaceLists)
1551                     .withKey(new ElanKey(elanInstanceName)).build();
1552             tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanInstanceName),
1553                     elanState, WriteTransaction.CREATE_MISSING_PARENTS);
1554         }
1555     }
1556
1557     private boolean isOperational(InterfaceInfo interfaceInfo) {
1558         if (interfaceInfo == null) {
1559             return false;
1560         }
1561         return interfaceInfo.getAdminState() == InterfaceInfo.InterfaceAdminState.ENABLED;
1562     }
1563
1564     @SuppressWarnings("checkstyle:IllegalCatch")
1565     public void handleInternalTunnelStateEvent(BigInteger srcDpId, BigInteger dstDpId) {
1566         ElanDpnInterfaces dpnInterfaceLists = elanUtils.getElanDpnInterfacesList();
1567         LOG.trace("processing tunnel state event for srcDpId {} dstDpId {}"
1568                 + " and dpnInterfaceList {}", srcDpId, dstDpId, dpnInterfaceLists);
1569         if (dpnInterfaceLists == null) {
1570             return;
1571         }
1572         List<ElanDpnInterfacesList> elanDpnIf = dpnInterfaceLists.getElanDpnInterfacesList();
1573         for (ElanDpnInterfacesList elanDpns : elanDpnIf) {
1574             int cnt = 0;
1575             String elanName = elanDpns.getElanInstanceName();
1576             ElanInstance elanInfo = elanInstanceCache.get(elanName).orNull();
1577             if (elanInfo == null) {
1578                 LOG.warn("ELAN Info is null for elanName {} that does exist in elanDpnInterfaceList, "
1579                         + "skipping this ELAN for tunnel handling", elanName);
1580                 continue;
1581             }
1582             if (!isVxlanNetworkOrVxlanSegment(elanInfo)) {
1583                 LOG.debug("Ignoring internal tunnel state event for Flat/Vlan elan {}", elanName);
1584                 continue;
1585             }
1586             List<DpnInterfaces> dpnInterfaces = elanDpns.getDpnInterfaces();
1587             if (dpnInterfaces == null) {
1588                 continue;
1589             }
1590             DpnInterfaces dstDpnIf = null;
1591             for (DpnInterfaces dpnIf : dpnInterfaces) {
1592                 BigInteger dpnIfDpId = dpnIf.getDpId();
1593                 if (dpnIfDpId.equals(srcDpId)) {
1594                     cnt++;
1595                 } else if (dpnIfDpId.equals(dstDpId)) {
1596                     cnt++;
1597                     dstDpnIf = dpnIf;
1598                 }
1599             }
1600             if (cnt == 2) {
1601                 LOG.info("Elan instance:{} is present b/w srcDpn:{} and dstDpn:{}", elanName, srcDpId, dstDpId);
1602                 final DpnInterfaces finalDstDpnIf = dstDpnIf; // var needs to be final so it can be accessed in lambda
1603                 jobCoordinator.enqueueJob(elanName, () -> {
1604                     // update Remote BC Group
1605                     LOG.trace("procesing elan remote bc group for tunnel event {}", elanInfo);
1606                     try {
1607                         elanL2GatewayMulticastUtils.setupElanBroadcastGroups(elanInfo, srcDpId);
1608                     } catch (RuntimeException e) {
1609                         LOG.error("Error while adding remote bc group for {} on dpId {} ", elanName, srcDpId);
1610                     }
1611                     Set<String> interfaceLists = new HashSet<>();
1612                     interfaceLists.addAll(finalDstDpnIf.getInterfaces());
1613                     for (String ifName : interfaceLists) {
1614                         jobCoordinator.enqueueJob(ElanUtils.getElanInterfaceJobKey(ifName), () -> {
1615                             LOG.info("Processing tunnel up event for elan {} and interface {}", elanName, ifName);
1616                             InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(ifName);
1617                             if (isOperational(interfaceInfo)) {
1618                                 return installDMacAddressTables(elanInfo, interfaceInfo, srcDpId);
1619                             }
1620                             return Collections.emptyList();
1621                         }, ElanConstants.JOB_MAX_RETRIES);
1622                     }
1623                     return Collections.emptyList();
1624                 }, ElanConstants.JOB_MAX_RETRIES);
1625             }
1626
1627         }
1628     }
1629
1630     /**
1631      * Handle external tunnel state event.
1632      *
1633      * @param externalTunnel
1634      *            the external tunnel
1635      * @param intrf
1636      *            the interface
1637      * @throws ElanException in case of issues creating the flow objects
1638      */
1639     public void handleExternalTunnelStateEvent(ExternalTunnel externalTunnel, Interface intrf) throws ElanException {
1640         if (!validateExternalTunnelStateEvent(externalTunnel, intrf)) {
1641             return;
1642         }
1643         // dpId/externalNodeId will be available either in source or destination
1644         // based on the tunnel end point
1645         BigInteger dpId = null;
1646         NodeId externalNodeId = null;
1647         if (StringUtils.isNumeric(externalTunnel.getSourceDevice())) {
1648             dpId = new BigInteger(externalTunnel.getSourceDevice());
1649             externalNodeId = new NodeId(externalTunnel.getDestinationDevice());
1650         } else if (StringUtils.isNumeric(externalTunnel.getDestinationDevice())) {
1651             dpId = new BigInteger(externalTunnel.getDestinationDevice());
1652             externalNodeId = new NodeId(externalTunnel.getSourceDevice());
1653         }
1654         if (dpId == null || externalNodeId == null) {
1655             LOG.error("Dp ID / externalNodeId not found in external tunnel {}", externalTunnel);
1656             return;
1657         }
1658
1659         ElanDpnInterfaces dpnInterfaceLists = elanUtils.getElanDpnInterfacesList();
1660         if (dpnInterfaceLists == null) {
1661             return;
1662         }
1663         List<ElanDpnInterfacesList> elanDpnIf = dpnInterfaceLists.getElanDpnInterfacesList();
1664         for (ElanDpnInterfacesList elanDpns : elanDpnIf) {
1665             String elanName = elanDpns.getElanInstanceName();
1666             ElanInstance elanInfo = elanInstanceCache.get(elanName).orNull();
1667
1668             DpnInterfaces dpnInterfaces = elanUtils.getElanInterfaceInfoByElanDpn(elanName, dpId);
1669             if (elanInfo == null || dpnInterfaces == null || dpnInterfaces.getInterfaces() == null
1670                     || dpnInterfaces.getInterfaces().isEmpty()) {
1671                 continue;
1672             }
1673             LOG.debug("Elan instance:{} is present in Dpn:{} ", elanName, dpId);
1674
1675             elanL2GatewayMulticastUtils.setupElanBroadcastGroups(elanInfo, dpId);
1676             // install L2gwDevices local macs in dpn.
1677             elanL2GatewayUtils.installL2gwDeviceMacsInDpn(dpId, externalNodeId, elanInfo, intrf.getName());
1678             // Install dpn macs on external device
1679             installDpnMacsInL2gwDevice(elanName, new HashSet<>(dpnInterfaces.getInterfaces()), dpId,
1680                     externalNodeId);
1681         }
1682         LOG.info("Handled ExternalTunnelStateEvent for {}", externalTunnel);
1683     }
1684
1685     /**
1686      * Installs dpn macs in external device. first it checks if the physical
1687      * locator towards this dpn tep is present or not if the physical locator is
1688      * present go ahead and add the ucast macs otherwise update the mcast mac
1689      * entry to include this dpn tep ip and schedule the job to put ucast macs
1690      * once the physical locator is programmed in device
1691      *
1692      * @param elanName
1693      *            the elan name
1694      * @param lstElanInterfaceNames
1695      *            the lst Elan interface names
1696      * @param dpnId
1697      *            the dpn id
1698      * @param externalNodeId
1699      *            the external node id
1700      */
1701     private void installDpnMacsInL2gwDevice(String elanName, Set<String> lstElanInterfaceNames, BigInteger dpnId,
1702             NodeId externalNodeId) {
1703         L2GatewayDevice elanL2GwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName,
1704                 externalNodeId.getValue());
1705         if (elanL2GwDevice == null) {
1706             LOG.debug("L2 gw device not found in elan cache for device name {}", externalNodeId);
1707             return;
1708         }
1709         IpAddress dpnTepIp = elanItmUtils.getSourceDpnTepIp(dpnId, externalNodeId);
1710         if (dpnTepIp == null) {
1711             LOG.warn("Could not install dpn macs in l2gw device , dpnTepIp not found dpn : {} , nodeid : {}", dpnId,
1712                     externalNodeId);
1713             return;
1714         }
1715
1716         String logicalSwitchName = ElanL2GatewayUtils.getLogicalSwitchFromElan(elanName);
1717         RemoteMcastMacs remoteMcastMac = elanL2GatewayUtils.readRemoteMcastMac(externalNodeId, logicalSwitchName,
1718                 LogicalDatastoreType.OPERATIONAL);
1719         boolean phyLocAlreadyExists =
1720                 ElanL2GatewayUtils.checkIfPhyLocatorAlreadyExistsInRemoteMcastEntry(externalNodeId, remoteMcastMac,
1721                 dpnTepIp);
1722         LOG.debug("phyLocAlreadyExists = {} for locator [{}] in remote mcast entry for elan [{}], nodeId [{}]",
1723                 phyLocAlreadyExists, String.valueOf(dpnTepIp.getValue()), elanName, externalNodeId.getValue());
1724         List<PhysAddress> staticMacs = elanL2GatewayUtils.getElanDpnMacsFromInterfaces(lstElanInterfaceNames);
1725
1726         if (phyLocAlreadyExists) {
1727             elanL2GatewayUtils.scheduleAddDpnMacsInExtDevice(elanName, dpnId, staticMacs, elanL2GwDevice);
1728             return;
1729         }
1730         elanL2GatewayMulticastUtils.scheduleMcastMacUpdateJob(elanName, elanL2GwDevice);
1731         elanL2GatewayUtils.scheduleAddDpnMacsInExtDevice(elanName, dpnId, staticMacs, elanL2GwDevice);
1732     }
1733
1734     /**
1735      * Validate external tunnel state event.
1736      *
1737      * @param externalTunnel
1738      *            the external tunnel
1739      * @param intrf
1740      *            the intrf
1741      * @return true, if successful
1742      */
1743     private boolean validateExternalTunnelStateEvent(ExternalTunnel externalTunnel, Interface intrf) {
1744         if (intrf.getOperStatus() == Interface.OperStatus.Up) {
1745             String srcDevice = externalTunnel.getDestinationDevice();
1746             String destDevice = externalTunnel.getSourceDevice();
1747             ExternalTunnel otherEndPointExtTunnel = elanUtils.getExternalTunnel(srcDevice, destDevice,
1748                     LogicalDatastoreType.CONFIGURATION);
1749             LOG.trace("Validating external tunnel state: src tunnel {}, dest tunnel {}", externalTunnel,
1750                     otherEndPointExtTunnel);
1751             if (otherEndPointExtTunnel != null) {
1752                 boolean otherEndPointInterfaceOperational = ElanUtils.isInterfaceOperational(
1753                         otherEndPointExtTunnel.getTunnelInterfaceName(), broker);
1754                 if (otherEndPointInterfaceOperational) {
1755                     return true;
1756                 } else {
1757                     LOG.debug("Other end [{}] of the external tunnel is not yet UP for {}",
1758                             otherEndPointExtTunnel.getTunnelInterfaceName(), externalTunnel);
1759                 }
1760             }
1761         }
1762         return false;
1763     }
1764
1765     private List<MatchInfo> getMatchesForFilterEqualsLPortTag(int lportTag) {
1766         List<MatchInfo> mkMatches = new ArrayList<>();
1767         // Matching metadata
1768         mkMatches.add(
1769                 new MatchMetadata(MetaDataUtil.getLportTagMetaData(lportTag), MetaDataUtil.METADATA_MASK_LPORT_TAG));
1770         mkMatches.add(new MatchTunnelId(BigInteger.valueOf(lportTag)));
1771         return mkMatches;
1772     }
1773
1774     @Override
1775     protected ElanInterfaceManager getDataTreeChangeListener() {
1776         return this;
1777     }
1778
1779     public void handleExternalInterfaceEvent(ElanInstance elanInstance, DpnInterfaces dpnInterfaces,
1780                                              BigInteger dpId) {
1781         LOG.debug("setting up remote BC group for elan {}", elanInstance.getPhysicalNetworkName());
1782         elanL2GatewayMulticastUtils.setupStandardElanBroadcastGroups(elanInstance, dpnInterfaces, dpId);
1783         try {
1784             Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL);
1785         } catch (InterruptedException e) {
1786             LOG.warn("Error while waiting for local BC group for ELAN {} to install", elanInstance);
1787         }
1788     }
1789 }