Java tutorial
/* * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.netvirt.elan.internal; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ListenableFuture; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Singleton; import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase; import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator; import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo; import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager; import org.opendaylight.genius.itm.globals.ITMConstants; import org.opendaylight.genius.mdsalutil.FlowEntity; import org.opendaylight.genius.mdsalutil.InstructionInfo; import org.opendaylight.genius.mdsalutil.MDSALUtil; import org.opendaylight.genius.mdsalutil.MatchInfo; import org.opendaylight.genius.mdsalutil.MetaDataUtil; import org.opendaylight.genius.mdsalutil.NwConstants; import org.opendaylight.genius.mdsalutil.actions.ActionDrop; import org.opendaylight.genius.mdsalutil.actions.ActionGroup; import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit; import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad; import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId; import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable; import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata; import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager; import org.opendaylight.genius.mdsalutil.matches.MatchMetadata; import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId; import org.opendaylight.genius.utils.ServiceIndex; import org.opendaylight.netvirt.elan.ElanException; import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils; import org.opendaylight.netvirt.elan.utils.ElanConstants; import org.opendaylight.netvirt.elan.utils.ElanForwardingEntriesHandler; import org.opendaylight.netvirt.elan.utils.ElanUtils; import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils; import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnel; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface.EtreeInterfaceType; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeLeafTagName; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanForwardingTables; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMacBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMacKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfacesBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfacesKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.forwarding.tables.MacTable; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.forwarding.tables.MacTableKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.elan._interface.StaticMacEntries; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.state.Elan; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.state.ElanBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.state.ElanKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntryBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.forwarding.entries.MacEntryKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class in charge of handling creations, modifications and removals of * ElanInterfaces. * * @see org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface */ @Singleton @SuppressWarnings("deprecation") public class ElanInterfaceManager extends AsyncDataTreeChangeListenerBase<ElanInterface, ElanInterfaceManager> implements AutoCloseable { private final DataBroker broker; private final IMdsalApiManager mdsalManager; private final IInterfaceManager interfaceManager; private final IdManagerService idManager; private final ElanForwardingEntriesHandler elanForwardingEntriesHandler; private ElanL2GatewayUtils elanL2GatewayUtils; private ElanUtils elanUtils; private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L); private final Map<String, ConcurrentLinkedQueue<ElanInterface>> unProcessedElanInterfaces = new ConcurrentHashMap<>(); private static final Logger LOG = LoggerFactory.getLogger(ElanInterfaceManager.class); @Inject public ElanInterfaceManager(final DataBroker dataBroker, final IdManagerService managerService, final IMdsalApiManager mdsalApiManager, IInterfaceManager interfaceManager, final ElanForwardingEntriesHandler elanForwardingEntriesHandler) { super(ElanInterface.class, ElanInterfaceManager.class); this.broker = dataBroker; this.idManager = managerService; this.mdsalManager = mdsalApiManager; this.interfaceManager = interfaceManager; this.elanForwardingEntriesHandler = elanForwardingEntriesHandler; } /** * This method is used instead of "regular" standard constructor dependency injection in, only, * ElanServiceProvider's constructor to wire things together. It was done like this because of the unholy * triumvirate of unhealthy love triangle between (at least) ElanUtils, ElanInterfaceManager and * ElanL2GatewayMulticastUtils. The proper solution to be able to get rid of this would be to split up some of these * relatively big classes into smaller classes, and then inject more fine grained among them. */ public void setElanUtils(ElanUtils elanUtils) { this.elanUtils = elanUtils; this.elanL2GatewayUtils = elanUtils.getElanL2GatewayUtils(); this.elanForwardingEntriesHandler.setElanUtils(elanUtils); } @Override @PostConstruct public void init() { registerListener(LogicalDatastoreType.CONFIGURATION, broker); } @Override protected InstanceIdentifier<ElanInterface> getWildCardPath() { return InstanceIdentifier.create(ElanInterfaces.class).child(ElanInterface.class); } @Override protected void remove(InstanceIdentifier<ElanInterface> identifier, ElanInterface del) { String interfaceName = del.getName(); ElanInstance elanInfo = ElanUtils.getElanInstanceByName(broker, del.getElanInstanceName()); /* * Handling in case the elan instance is deleted.If the Elan instance is * deleted, there is no need to explicitly delete the elan interfaces */ if (elanInfo == null) { return; } InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName); String elanInstanceName = elanInfo.getElanInstanceName(); DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance(); InterfaceRemoveWorkerOnElan configWorker = new InterfaceRemoveWorkerOnElan(elanInstanceName, elanInfo, interfaceName, interfaceInfo, false, this); coordinator.enqueueJob(elanInstanceName, configWorker, ElanConstants.JOB_MAX_RETRIES); } public void removeElanInterface(List<ListenableFuture<Void>> futures, ElanInstance elanInfo, String interfaceName, InterfaceInfo interfaceInfo, boolean isInterfaceStateRemoved) { String elanName = elanInfo.getElanInstanceName(); boolean isLastElanInterface = false; boolean isLastInterfaceOnDpn = false; BigInteger dpId = null; long elanTag = elanInfo.getElanTag(); WriteTransaction tx = broker.newWriteOnlyTransaction(); WriteTransaction deleteFlowGroupTx = broker.newWriteOnlyTransaction(); Elan elanState = removeElanStateForInterface(elanInfo, interfaceName, tx); if (elanState == null) { return; } List<String> elanInterfaces = elanState.getElanInterfaces(); if (elanInterfaces.size() == 0) { isLastElanInterface = true; } if (interfaceInfo != null) { dpId = interfaceInfo.getDpId(); DpnInterfaces dpnInterfaces = removeElanDpnInterfaceFromOperationalDataStore(elanName, dpId, interfaceName, elanTag, tx); /* * If there are not elan ports, remove the unknown dmac, terminating * service table flows, remote/local bc group */ if (dpnInterfaces == null || dpnInterfaces.getInterfaces() == null || dpnInterfaces.getInterfaces().isEmpty()) { // No more Elan Interfaces in this DPN LOG.debug("deleting the elan: {} present on dpId: {}", elanInfo.getElanInstanceName(), dpId); removeDefaultTermFlow(dpId, elanInfo.getElanTag()); removeUnknownDmacFlow(dpId, elanInfo, deleteFlowGroupTx, elanInfo.getElanTag()); removeEtreeUnknownDmacFlow(dpId, elanInfo, deleteFlowGroupTx); removeElanBroadcastGroup(elanInfo, interfaceInfo, deleteFlowGroupTx); removeLocalBroadcastGroup(elanInfo, interfaceInfo, deleteFlowGroupTx); removeEtreeBroadcastGrups(elanInfo, interfaceInfo, deleteFlowGroupTx); if (ElanUtils.isVxlan(elanInfo) || ElanUtils.isVxlanSegment(elanInfo)) { unsetExternalTunnelTable(dpId, elanInfo); } isLastInterfaceOnDpn = true; } else { setupLocalBroadcastGroups(elanInfo, dpnInterfaces, interfaceInfo); } } futures.add(ElanUtils.waitForTransactionToComplete(tx)); futures.add(ElanUtils.waitForTransactionToComplete(deleteFlowGroupTx)); if (isLastInterfaceOnDpn && dpId != null && (ElanUtils.isVxlan(elanInfo) || ElanUtils.isVxlanSegment(elanInfo))) { setElanAndEtreeBCGrouponOtherDpns(elanInfo, dpId); } DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance(); InterfaceRemoveWorkerOnElanInterface removeInterfaceWorker = new InterfaceRemoveWorkerOnElanInterface( interfaceName, elanInfo, interfaceInfo, isInterfaceStateRemoved, this, isLastElanInterface); coordinator.enqueueJob(interfaceName, removeInterfaceWorker, ElanConstants.JOB_MAX_RETRIES); } private void removeEtreeUnknownDmacFlow(BigInteger dpId, ElanInstance elanInfo, WriteTransaction deleteFlowGroupTx) { EtreeLeafTagName etreeLeafTag = elanUtils.getEtreeLeafTagByElanTag(elanInfo.getElanTag()); if (etreeLeafTag != null) { long leafTag = etreeLeafTag.getEtreeLeafTag().getValue(); removeUnknownDmacFlow(dpId, elanInfo, deleteFlowGroupTx, leafTag); } } private void removeEtreeBroadcastGrups(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction deleteFlowGroupTx) { removeLeavesEtreeBroadcastGroup(elanInfo, interfaceInfo, deleteFlowGroupTx); removeLeavesLocalBroadcastGroup(elanInfo, interfaceInfo, deleteFlowGroupTx); } private void removeLeavesLocalBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction deleteFlowGroupTx) { EtreeInstance etreeInstance = elanInfo.getAugmentation(EtreeInstance.class); if (etreeInstance != null) { BigInteger dpnId = interfaceInfo.getDpId(); long groupId = ElanUtils.getEtreeLeafLocalBCGId(etreeInstance.getEtreeLeafTagVal().getValue()); List<Bucket> listBuckets = new ArrayList<>(); int bucketId = 0; listBuckets.add(getLocalBCGroupBucketInfo(interfaceInfo, bucketId)); Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBuckets)); LOG.trace("deleted the localBroadCast Group:{}", group); mdsalManager.removeGroupToTx(dpnId, group, deleteFlowGroupTx); } } private void removeLeavesEtreeBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction deleteFlowGroupTx) { EtreeInstance etreeInstance = elanInfo.getAugmentation(EtreeInstance.class); if (etreeInstance != null) { long etreeTag = etreeInstance.getEtreeLeafTagVal().getValue(); int bucketId = 0; int actionKey = 0; List<Bucket> listBuckets = new ArrayList<>(); List<Action> listAction = new ArrayList<>(); listAction.add(new ActionGroup(ElanUtils.getEtreeLeafLocalBCGId(etreeTag)).buildAction(++actionKey)); listBuckets.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; listBuckets.addAll(getRemoteBCGroupBucketInfos(elanInfo, bucketId, interfaceInfo, etreeTag)); BigInteger dpnId = interfaceInfo.getDpId(); long groupId = ElanUtils.getEtreeLeafRemoteBCGId(etreeTag); Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBuckets)); LOG.trace("deleting the remoteBroadCast group:{}", group); mdsalManager.removeGroupToTx(dpnId, group, deleteFlowGroupTx); } } private Elan removeElanStateForInterface(ElanInstance elanInfo, String interfaceName, WriteTransaction tx) { String elanName = elanInfo.getElanInstanceName(); Elan elanState = ElanUtils.getElanByName(broker, elanName); if (elanState == null) { return elanState; } List<String> elanInterfaces = elanState.getElanInterfaces(); elanInterfaces.remove(interfaceName); if (elanInterfaces.isEmpty()) { tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanName)); tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanMacTableOperationalDataPath(elanName)); tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInfoEntriesOperationalDataPath(elanInfo.getElanTag())); } else { Elan updateElanState = new ElanBuilder().setElanInterfaces(elanInterfaces).setName(elanName) .setKey(new ElanKey(elanName)).build(); tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanName), updateElanState); } return elanState; } private void deleteElanInterfaceFromConfigDS(String interfaceName, WriteTransaction tx) { // removing the ElanInterface from the config data_store if interface is // not present in Interface config DS if (interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName) == null && ElanUtils.getElanInterfaceByElanInterfaceName(broker, interfaceName) != null) { tx.delete(LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName)); } } void removeEntriesForElanInterface(List<ListenableFuture<Void>> futures, ElanInstance elanInfo, InterfaceInfo interfaceInfo, String interfaceName, boolean isInterfaceStateRemoved, boolean isLastElanInterface) { String elanName = elanInfo.getElanInstanceName(); WriteTransaction tx = broker.newWriteOnlyTransaction(); WriteTransaction deleteFlowGroupTx = broker.newWriteOnlyTransaction(); InstanceIdentifier<ElanInterfaceMac> elanInterfaceId = ElanUtils .getElanInterfaceMacEntriesOperationalDataPath(interfaceName); Optional<ElanInterfaceMac> existingElanInterfaceMac = elanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanInterfaceId); LOG.debug("Removing the Interface:{} from elan:{}", interfaceName, elanName); if (interfaceInfo != null) { if (existingElanInterfaceMac.isPresent()) { List<MacEntry> existingMacEntries = existingElanInterfaceMac.get().getMacEntry(); List<MacEntry> macEntries = new ArrayList<>(); if (existingMacEntries != null && !existingMacEntries.isEmpty()) { macEntries.addAll(existingMacEntries); } List<PhysAddress> macAddresses = macEntries.stream().map(macEntry -> { PhysAddress macAddress = macEntry.getMacAddress(); LOG.debug("removing the mac-entry:{} present on elanInterface:{}", macAddress.getValue(), interfaceName); Optional<MacEntry> macEntryOptional = elanUtils.getMacEntryForElanInstance(elanName, macAddress); if (!isLastElanInterface && macEntryOptional.isPresent()) { tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getMacEntryOperationalDataPath(elanName, macAddress)); } elanUtils.deleteMacFlows(elanInfo, interfaceInfo, macEntry, deleteFlowGroupTx); return macAddress; }).collect(Collectors.toList()); // Removing all those MACs from External Devices belonging // to this ELAN if ((ElanUtils.isVxlan(elanInfo) || (ElanUtils.isVxlanSegment(elanInfo))) && !macAddresses.isEmpty()) { elanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo, macAddresses); } } removeDefaultTermFlow(interfaceInfo.getDpId(), interfaceInfo.getInterfaceTag()); removeFilterEqualsTable(elanInfo, interfaceInfo, deleteFlowGroupTx); } else if (existingElanInterfaceMac.isPresent()) { // Interface does not exist in ConfigDS, so lets remove everything // about that interface related to Elan java.util.Optional.ofNullable(existingElanInterfaceMac.get().getMacEntry()) .ifPresent(macEntries -> macEntries.stream().forEach(macEntry -> { PhysAddress macAddress = macEntry.getMacAddress(); Optional<MacEntry> macEntryOptional = elanUtils.getMacEntryForElanInstance(elanName, macAddress); if (macEntryOptional.isPresent()) { tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getMacEntryOperationalDataPath(elanName, macAddress)); } })); } if (existingElanInterfaceMac.isPresent()) { tx.delete(LogicalDatastoreType.OPERATIONAL, elanInterfaceId); } if (!isInterfaceStateRemoved) { unbindService(elanInfo, interfaceName, tx); } deleteElanInterfaceFromConfigDS(interfaceName, tx); futures.add(ElanUtils.waitForTransactionToComplete(tx)); futures.add(ElanUtils.waitForTransactionToComplete(deleteFlowGroupTx)); } private DpnInterfaces removeElanDpnInterfaceFromOperationalDataStore(String elanName, BigInteger dpId, String interfaceName, long elanTag, WriteTransaction tx) { DpnInterfaces dpnInterfaces = elanUtils.getElanInterfaceInfoByElanDpn(elanName, dpId); if (dpnInterfaces != null) { List<String> interfaceLists = dpnInterfaces.getInterfaces(); interfaceLists.remove(interfaceName); if (interfaceLists == null || interfaceLists.isEmpty()) { deleteAllRemoteMacsInADpn(elanName, dpId, elanTag); deleteElanDpnInterface(elanName, dpId, tx); } else { dpnInterfaces = updateElanDpnInterfacesList(elanName, dpId, interfaceLists, tx); } } return dpnInterfaces; } private void deleteAllRemoteMacsInADpn(String elanName, BigInteger dpId, long elanTag) { List<DpnInterfaces> dpnInterfaces = elanUtils.getInvolvedDpnsInElan(elanName); for (DpnInterfaces dpnInterface : dpnInterfaces) { BigInteger currentDpId = dpnInterface.getDpId(); if (!currentDpId.equals(dpId)) { for (String elanInterface : dpnInterface.getInterfaces()) { ElanInterfaceMac macs = elanUtils.getElanInterfaceMacByInterfaceName(elanInterface); if (macs == null || macs.getMacEntry() == null) { continue; } for (MacEntry mac : macs.getMacEntry()) { removeTheMacFlowInTheDPN(dpId, elanTag, currentDpId, mac); removeEtreeMacFlowInTheDPN(dpId, elanTag, currentDpId, mac); } } } } } private void removeEtreeMacFlowInTheDPN(BigInteger dpId, long elanTag, BigInteger currentDpId, MacEntry mac) { EtreeLeafTagName etreeLeafTag = elanUtils.getEtreeLeafTagByElanTag(elanTag); if (etreeLeafTag != null) { removeTheMacFlowInTheDPN(dpId, etreeLeafTag.getEtreeLeafTag().getValue(), currentDpId, mac); } } private void removeTheMacFlowInTheDPN(BigInteger dpId, long elanTag, BigInteger currentDpId, MacEntry mac) { mdsalManager.removeFlow(dpId, MDSALUtil.buildFlow(NwConstants.ELAN_DMAC_TABLE, ElanUtils.getKnownDynamicmacFlowRef( NwConstants.ELAN_DMAC_TABLE, dpId, currentDpId, mac.getMacAddress().getValue(), elanTag))); } /* * Possible Scenarios for update * a. if orig={1,2,3,4} and updated=null or updated={} then all {1,2,3,4} should be removed b. if orig=null or orig={} and updated ={1,2,3,4} then all {1,2,3,4} should be added c. if orig = {1,2,3,4} updated={2,3,4} then 1 should be removed d. basically if orig = { 1,2,3,4} and updated is {1,2,3,4,5} then we should just add 5 e. if orig = {1,2,3,4} updated={2,3,4,5} then 1 should be removed , 5 should be added * */ @Override protected void update(InstanceIdentifier<ElanInterface> identifier, ElanInterface original, ElanInterface update) { // updating the static-Mac Entries for the existing elanInterface String elanName = update.getElanInstanceName(); String interfaceName = update.getName(); List<StaticMacEntries> originalStaticMacEntries = original.getStaticMacEntries(); List<StaticMacEntries> updatedStaticMacEntries = update.getStaticMacEntries(); List<StaticMacEntries> deletedEntries = elanUtils.diffOf(originalStaticMacEntries, updatedStaticMacEntries); List<StaticMacEntries> updatedEntries = elanUtils.diffOf(updatedStaticMacEntries, originalStaticMacEntries); deletedEntries.forEach((deletedEntry) -> removeInterfaceStaticMacEntries(elanName, interfaceName, deletedEntry.getMacAddress())); /*if updatedStaticMacEntries is NOT NULL, which means as part of update call these entries were added. * Hence add the macentries for the same.*/ for (StaticMacEntries staticMacEntry : updatedEntries) { InstanceIdentifier<MacEntry> macEntryIdentifier = getMacEntryOperationalDataPath(elanName, staticMacEntry.getMacAddress()); Optional<MacEntry> existingMacEntry = elanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, macEntryIdentifier); WriteTransaction tx = broker.newWriteOnlyTransaction(); if (existingMacEntry.isPresent()) { elanForwardingEntriesHandler.updateElanInterfaceForwardingTablesList(elanName, interfaceName, existingMacEntry.get().getInterface(), existingMacEntry.get(), tx); } else { elanForwardingEntriesHandler.addElanInterfaceForwardingTableList( ElanUtils.getElanInstanceByName(broker, elanName), interfaceName, staticMacEntry, tx); } ElanUtils.waitForTransactionToComplete(tx); } } @Override protected void add(InstanceIdentifier<ElanInterface> identifier, ElanInterface elanInterfaceAdded) { String elanInstanceName = elanInterfaceAdded.getElanInstanceName(); String interfaceName = elanInterfaceAdded.getName(); InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName); if (interfaceInfo == null) { LOG.warn("Interface {} is removed from Interface Oper DS due to port down ", interfaceName); return; } ElanInstance elanInstance = ElanUtils.getElanInstanceByName(broker, elanInstanceName); if (elanInstance == null) { elanInstance = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName) .setDescription(elanInterfaceAdded.getDescription()).build(); // Add the ElanInstance in the Configuration data-store WriteTransaction tx = broker.newWriteOnlyTransaction(); List<String> elanInterfaces = new ArrayList<>(); elanInterfaces.add(interfaceName); ElanUtils.updateOperationalDataStore(broker, idManager, elanInstance, elanInterfaces, tx); ElanUtils.waitForTransactionToComplete(tx); elanInstance = ElanUtils.getElanInstanceByName(broker, elanInstanceName); } Long elanTag = elanInstance.getElanTag(); // If elan tag is not updated, then put the elan interface into // unprocessed entry map and entry. Let entries // in this map get processed during ELAN update DCN. if (elanTag == null) { ConcurrentLinkedQueue<ElanInterface> elanInterfaces = unProcessedElanInterfaces.get(elanInstanceName); if (elanInterfaces == null) { elanInterfaces = new ConcurrentLinkedQueue<>(); } elanInterfaces.add(elanInterfaceAdded); unProcessedElanInterfaces.put(elanInstanceName, elanInterfaces); return; } DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance(); InterfaceAddWorkerOnElan addWorker = new InterfaceAddWorkerOnElan(elanInstanceName, elanInterfaceAdded, interfaceInfo, elanInstance, this); coordinator.enqueueJob(elanInstanceName, addWorker, ElanConstants.JOB_MAX_RETRIES); } List<ListenableFuture<Void>> handleunprocessedElanInterfaces(ElanInstance elanInstance) throws ElanException { List<ListenableFuture<Void>> futures = new ArrayList<>(); Queue<ElanInterface> elanInterfaces = unProcessedElanInterfaces.get(elanInstance.getElanInstanceName()); if (elanInterfaces == null || elanInterfaces.isEmpty()) { return futures; } for (ElanInterface elanInterface : elanInterfaces) { String interfaceName = elanInterface.getName(); InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName); addElanInterface(futures, elanInterface, interfaceInfo, elanInstance); } return futures; } void programRemoteDmacFlow(ElanInstance elanInstance, InterfaceInfo interfaceInfo, WriteTransaction writeFlowGroupTx) throws ElanException { ElanDpnInterfacesList elanDpnInterfacesList = elanUtils .getElanDpnInterfacesList(elanInstance.getElanInstanceName()); List<DpnInterfaces> dpnInterfaceLists = null; if (elanDpnInterfacesList != null) { dpnInterfaceLists = elanDpnInterfacesList.getDpnInterfaces(); } if (dpnInterfaceLists == null) { dpnInterfaceLists = new ArrayList<>(); } for (DpnInterfaces dpnInterfaces : dpnInterfaceLists) { BigInteger dstDpId = interfaceInfo.getDpId(); if (dpnInterfaces.getDpId().equals(dstDpId)) { continue; } List<String> remoteElanInterfaces = dpnInterfaces.getInterfaces(); for (String remoteIf : remoteElanInterfaces) { ElanInterfaceMac elanIfMac = elanUtils.getElanInterfaceMacByInterfaceName(remoteIf); InterfaceInfo remoteInterface = interfaceManager.getInterfaceInfo(remoteIf); if (elanIfMac == null) { continue; } List<MacEntry> remoteMacEntries = elanIfMac.getMacEntry(); if (remoteMacEntries != null) { for (MacEntry macEntry : remoteMacEntries) { String macAddress = macEntry.getMacAddress().getValue(); LOG.info("Programming remote dmac {} on the newly added DPN {} for elan {}", macAddress, dstDpId, elanInstance.getElanInstanceName()); elanUtils.setupRemoteDmacFlow(dstDpId, remoteInterface.getDpId(), remoteInterface.getInterfaceTag(), elanInstance.getElanTag(), macAddress, elanInstance.getElanInstanceName(), writeFlowGroupTx, remoteIf, elanInstance); } } } } } void addElanInterface(List<ListenableFuture<Void>> futures, ElanInterface elanInterface, InterfaceInfo interfaceInfo, ElanInstance elanInstance) throws ElanException { Preconditions.checkNotNull(elanInstance, "elanInstance cannot be null"); Preconditions.checkNotNull(interfaceInfo, "interfaceInfo cannot be null"); Preconditions.checkNotNull(elanInterface, "elanInterface cannot be null"); String interfaceName = elanInterface.getName(); String elanInstanceName = elanInterface.getElanInstanceName(); Elan elanInfo = ElanUtils.getElanByName(broker, elanInstanceName); WriteTransaction tx = broker.newWriteOnlyTransaction(); if (elanInfo == null) { List<String> elanInterfaces = new ArrayList<>(); elanInterfaces.add(interfaceName); ElanUtils.updateOperationalDataStore(broker, idManager, elanInstance, elanInterfaces, tx); } else { createElanStateList(elanInstanceName, interfaceName, tx); } boolean isFirstInterfaceInDpn = false; // Specific actions to the DPN where the ElanInterface has been added, // for example, programming the // External tunnel table if needed or adding the ElanInterface to the // DpnInterfaces in the operational DS. BigInteger dpId = interfaceInfo != null ? dpId = interfaceInfo.getDpId() : null; DpnInterfaces dpnInterfaces = null; if (dpId != null && !dpId.equals(ElanConstants.INVALID_DPN)) { InstanceIdentifier<DpnInterfaces> elanDpnInterfaces = ElanUtils .getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId); Optional<DpnInterfaces> existingElanDpnInterfaces = elanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaces); if (!existingElanDpnInterfaces.isPresent()) { isFirstInterfaceInDpn = true; // ELAN's 1st ElanInterface added to this DPN dpnInterfaces = createElanInterfacesList(elanInstanceName, interfaceName, dpId, tx); // The 1st ElanInterface in a DPN must program the Ext Tunnel // table, but only if Elan has VNI if ((ElanUtils.isVxlan(elanInstance) || (ElanUtils.isVxlanSegment(elanInstance)))) { setExternalTunnelTable(dpId, elanInstance); } elanL2GatewayUtils.installElanL2gwDevicesLocalMacsInDpn(dpId, elanInstance, interfaceName); } else { List<String> elanInterfaces = existingElanDpnInterfaces.get().getInterfaces(); elanInterfaces.add(interfaceName); if (elanInterfaces.size() == 1) { // 1st dpn interface elanL2GatewayUtils.installElanL2gwDevicesLocalMacsInDpn(dpId, elanInstance, interfaceName); } dpnInterfaces = updateElanDpnInterfacesList(elanInstanceName, dpId, elanInterfaces, tx); } } // add code to install Local/Remote BC group, unknow DMAC entry, // terminating service table flow entry // call bindservice of interfacemanager to create ingress table flow // enty. // Add interface to the ElanInterfaceForwardingEntires Container createElanInterfaceTablesList(interfaceName, tx); if (interfaceInfo != null) { installEntriesForFirstInterfaceonDpn(elanInstance, interfaceInfo, dpnInterfaces, isFirstInterfaceInDpn, tx); } futures.add(ElanUtils.waitForTransactionToComplete(tx)); if (isFirstInterfaceInDpn && (ElanUtils.isVxlan(elanInstance) || ElanUtils.isVxlanSegment(elanInstance))) { //update the remote-DPNs remoteBC group entry with Tunnels LOG.trace("update remote bc group for elan {} on other DPNs for newly added dpn {}", elanInstance, dpId); setElanAndEtreeBCGrouponOtherDpns(elanInstance, dpId); } DataStoreJobCoordinator coordinator = DataStoreJobCoordinator.getInstance(); InterfaceAddWorkerOnElanInterface addWorker = new InterfaceAddWorkerOnElanInterface(interfaceName, elanInterface, interfaceInfo, elanInstance, isFirstInterfaceInDpn, this); coordinator.enqueueJob(interfaceName, addWorker, ElanConstants.JOB_MAX_RETRIES); } void setupEntriesForElanInterface(List<ListenableFuture<Void>> futures, ElanInstance elanInstance, ElanInterface elanInterface, InterfaceInfo interfaceInfo, boolean isFirstInterfaceInDpn) throws ElanException { String elanInstanceName = elanInstance.getElanInstanceName(); String interfaceName = elanInterface.getName(); WriteTransaction tx = broker.newWriteOnlyTransaction(); BigInteger dpId = interfaceInfo.getDpId(); WriteTransaction writeFlowGroupTx = broker.newWriteOnlyTransaction(); installEntriesForElanInterface(elanInstance, elanInterface, interfaceInfo, isFirstInterfaceInDpn, tx, writeFlowGroupTx); List<StaticMacEntries> staticMacEntriesList = elanInterface.getStaticMacEntries(); List<PhysAddress> staticMacAddresses = Lists.newArrayList(); if (ElanUtils.isNotEmpty(staticMacEntriesList)) { boolean isInterfaceOperational = isOperational(interfaceInfo); for (StaticMacEntries staticMacEntry : staticMacEntriesList) { InstanceIdentifier<MacEntry> macId = getMacEntryOperationalDataPath(elanInstanceName, staticMacEntry.getMacAddress()); Optional<MacEntry> existingMacEntry = elanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, macId); if (existingMacEntry.isPresent()) { elanForwardingEntriesHandler.updateElanInterfaceForwardingTablesList(elanInstanceName, interfaceName, existingMacEntry.get().getInterface(), existingMacEntry.get(), tx); } else { elanForwardingEntriesHandler.addElanInterfaceForwardingTableList(elanInstance, interfaceName, staticMacEntry, tx); } if (isInterfaceOperational) { // Setting SMAC, DMAC, UDMAC in this DPN and also in other // DPNs String macAddress = staticMacEntry.getMacAddress().getValue(); LOG.info( "programming smac and dmacs for {} on source and other DPNs for elan {} and interface {}", macAddress, elanInstanceName, interfaceName); elanUtils.setupMacFlows(elanInstance, interfaceInfo, ElanConstants.STATIC_MAC_TIMEOUT, staticMacEntry.getMacAddress().getValue(), true, writeFlowGroupTx); } } if (isInterfaceOperational) { // Add MAC in TOR's remote MACs via OVSDB. Outside of the loop // on purpose. for (StaticMacEntries staticMacEntry : staticMacEntriesList) { staticMacAddresses.add(staticMacEntry.getMacAddress()); } elanL2GatewayUtils.scheduleAddDpnMacInExtDevices(elanInstance.getElanInstanceName(), dpId, staticMacAddresses); } } futures.add(ElanUtils.waitForTransactionToComplete(tx)); futures.add(ElanUtils.waitForTransactionToComplete(writeFlowGroupTx)); } protected void removeInterfaceStaticMacEntries(String elanInstanceName, String interfaceName, PhysAddress physAddress) { InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName); InstanceIdentifier<MacEntry> macId = getMacEntryOperationalDataPath(elanInstanceName, physAddress); Optional<MacEntry> existingMacEntry = elanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, macId); if (!existingMacEntry.isPresent()) { return; } MacEntry macEntry = new MacEntryBuilder().setMacAddress(physAddress).setInterface(interfaceName) .setKey(new MacEntryKey(physAddress)).build(); WriteTransaction tx = broker.newWriteOnlyTransaction(); elanForwardingEntriesHandler.deleteElanInterfaceForwardingEntries( ElanUtils.getElanInstanceByName(broker, elanInstanceName), interfaceInfo, macEntry, tx); ElanUtils.waitForTransactionToComplete(tx); } private InstanceIdentifier<MacEntry> getMacEntryOperationalDataPath(String elanName, PhysAddress physAddress) { return InstanceIdentifier.builder(ElanForwardingTables.class) .child(MacTable.class, new MacTableKey(elanName)) .child(MacEntry.class, new MacEntryKey(physAddress)).build(); } private void installEntriesForElanInterface(ElanInstance elanInstance, ElanInterface elanInterface, InterfaceInfo interfaceInfo, boolean isFirstInterfaceInDpn, WriteTransaction tx, WriteTransaction writeFlowGroupTx) throws ElanException { if (!isOperational(interfaceInfo)) { return; } BigInteger dpId = interfaceInfo.getDpId(); elanUtils.setupTermDmacFlows(interfaceInfo, mdsalManager, writeFlowGroupTx); setupFilterEqualsTable(elanInstance, interfaceInfo, writeFlowGroupTx); if (isFirstInterfaceInDpn) { // Terminating Service , UnknownDMAC Table. setupTerminateServiceTable(elanInstance, dpId, writeFlowGroupTx); setupUnknownDMacTable(elanInstance, dpId, writeFlowGroupTx); /* * Install remote DMAC flow. This is required since this DPN is * added later to the elan instance and remote DMACs of other * interfaces in this elan instance are not present in the current * dpn. */ if (!interfaceManager.isExternalInterface(interfaceInfo.getInterfaceName())) { LOG.info("Programming remote dmac flows on the newly connected dpn {} for elan {} ", dpId, elanInstance.getElanInstanceName()); programRemoteDmacFlow(elanInstance, interfaceInfo, writeFlowGroupTx); } } // bind the Elan service to the Interface bindService(elanInstance, elanInterface, interfaceInfo.getInterfaceTag(), tx); } public void installEntriesForFirstInterfaceonDpn(ElanInstance elanInfo, InterfaceInfo interfaceInfo, DpnInterfaces dpnInterfaces, boolean isFirstInterfaceInDpn, WriteTransaction tx) { if (!isOperational(interfaceInfo)) { return; } // LocalBroadcast Group creation with elan-Interfaces setupLocalBroadcastGroups(elanInfo, dpnInterfaces, interfaceInfo); if (isFirstInterfaceInDpn) { LOG.trace("waitTimeForSyncInstall is {}", WAIT_TIME_FOR_SYNC_INSTALL); BigInteger dpId = interfaceInfo.getDpId(); // RemoteBroadcast Group creation try { Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL); } catch (InterruptedException e1) { LOG.warn("Error while waiting for local BC group for ELAN {} to install", elanInfo); } setupElanBroadcastGroups(elanInfo, dpnInterfaces, dpId); try { Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL); } catch (InterruptedException e1) { LOG.warn("Error while waiting for local BC group for ELAN {} to install", elanInfo); } } } public void setupFilterEqualsTable(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction writeFlowGroupTx) { int ifTag = interfaceInfo.getInterfaceTag(); Flow flow = MDSALUtil.buildFlowNew(NwConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(NwConstants.ELAN_FILTER_EQUALS_TABLE, ifTag), 9, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getTunnelIdMatchForFilterEqualsLPortTag(ifTag), elanUtils.getInstructionsInPortForOutGroup(interfaceInfo.getInterfaceName())); mdsalManager.addFlowToTx(interfaceInfo.getDpId(), flow, writeFlowGroupTx); Flow flowEntry = MDSALUtil.buildFlowNew(NwConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(NwConstants.ELAN_FILTER_EQUALS_TABLE, 1000 + ifTag), 10, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getMatchesForFilterEqualsLPortTag(ifTag), MDSALUtil.buildInstructionsDrop()); mdsalManager.addFlowToTx(interfaceInfo.getDpId(), flowEntry, writeFlowGroupTx); } public void removeFilterEqualsTable(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction deleteFlowGroupTx) { int ifTag = interfaceInfo.getInterfaceTag(); Flow flow = MDSALUtil.buildFlowNew(NwConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(NwConstants.ELAN_FILTER_EQUALS_TABLE, ifTag), 9, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getTunnelIdMatchForFilterEqualsLPortTag(ifTag), elanUtils.getInstructionsInPortForOutGroup(interfaceInfo.getInterfaceName())); mdsalManager.removeFlowToTx(interfaceInfo.getDpId(), flow, deleteFlowGroupTx); Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(NwConstants.ELAN_FILTER_EQUALS_TABLE, 1000 + ifTag), 10, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getMatchesForFilterEqualsLPortTag(ifTag), MDSALUtil.buildInstructionsDrop()); mdsalManager.removeFlowToTx(interfaceInfo.getDpId(), flowEntity, deleteFlowGroupTx); } private List<Bucket> getRemoteBCGroupBucketInfos(ElanInstance elanInfo, int bucketKeyStart, InterfaceInfo interfaceInfo, long elanTag) { return getRemoteBCGroupBuckets(elanInfo, null, interfaceInfo.getDpId(), bucketKeyStart, elanTag); } public List<Bucket> getRemoteBCGroupBuckets(ElanInstance elanInfo, DpnInterfaces dpnInterfaces, BigInteger dpnId, int bucketId, long elanTag) { List<Bucket> listBucketInfo = new ArrayList<>(); ElanDpnInterfacesList elanDpns = elanUtils.getElanDpnInterfacesList(elanInfo.getElanInstanceName()); if (ElanUtils.isVxlan(elanInfo)) { listBucketInfo.addAll(getRemoteBCGroupTunnelBuckets(elanDpns, dpnId, bucketId, elanTag)); } listBucketInfo.addAll(getRemoteBCGroupExternalPortBuckets(elanDpns, dpnInterfaces, dpnId, getNextAvailableBucketId(listBucketInfo.size()))); listBucketInfo.addAll(getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId, getNextAvailableBucketId(listBucketInfo.size()))); return listBucketInfo; } private int getNextAvailableBucketId(int bucketSize) { return bucketSize + 1; } @SuppressWarnings("checkstyle:IllegalCatch") private List<Bucket> getRemoteBCGroupTunnelBuckets(ElanDpnInterfacesList elanDpns, BigInteger dpnId, int bucketId, long elanTag) { List<Bucket> listBucketInfo = new ArrayList<>(); if (elanDpns != null) { for (DpnInterfaces dpnInterface : elanDpns.getDpnInterfaces()) { if (elanUtils.isDpnPresent(dpnInterface.getDpId()) && !Objects.equals(dpnInterface.getDpId(), dpnId) && dpnInterface.getInterfaces() != null && !dpnInterface.getInterfaces().isEmpty()) { try { List<Action> listActionInfo = elanUtils.getInternalTunnelItmEgressAction(dpnId, dpnInterface.getDpId(), elanTag); if (listActionInfo.isEmpty()) { continue; } listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; } catch (Exception ex) { LOG.error( "Logical Group Interface not found between source Dpn - {}, destination Dpn - {} ", dpnId, dpnInterface.getDpId(), ex); } } } } return listBucketInfo; } private List<Bucket> getRemoteBCGroupExternalPortBuckets(ElanDpnInterfacesList elanDpns, DpnInterfaces dpnInterfaces, BigInteger dpnId, int bucketId) { DpnInterfaces currDpnInterfaces = dpnInterfaces != null ? dpnInterfaces : getDpnInterfaces(elanDpns, dpnId); if (currDpnInterfaces == null || !elanUtils.isDpnPresent(currDpnInterfaces.getDpId()) || currDpnInterfaces.getInterfaces() == null || currDpnInterfaces.getInterfaces().isEmpty()) { return Collections.emptyList(); } List<Bucket> listBucketInfo = new ArrayList<>(); for (String interfaceName : currDpnInterfaces.getInterfaces()) { if (interfaceManager.isExternalInterface(interfaceName)) { List<Action> listActionInfo = elanUtils.getExternalPortItmEgressAction(interfaceName); if (!listActionInfo.isEmpty()) { listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; } } } return listBucketInfo; } private DpnInterfaces getDpnInterfaces(ElanDpnInterfacesList elanDpns, BigInteger dpnId) { if (elanDpns != null) { for (DpnInterfaces dpnInterface : elanDpns.getDpnInterfaces()) { if (dpnInterface.getDpId().equals(dpnId)) { return dpnInterface; } } } return null; } private void setElanAndEtreeBCGrouponOtherDpns(ElanInstance elanInfo, BigInteger dpId) { int elanTag = elanInfo.getElanTag().intValue(); long groupId = ElanUtils.getElanRemoteBCGId(elanTag); setBCGrouponOtherDpns(elanInfo, dpId, elanTag, groupId); EtreeInstance etreeInstance = elanInfo.getAugmentation(EtreeInstance.class); if (etreeInstance != null) { int etreeLeafTag = etreeInstance.getEtreeLeafTagVal().getValue().intValue(); long etreeLeafGroupId = ElanUtils.getEtreeLeafRemoteBCGId(etreeLeafTag); setBCGrouponOtherDpns(elanInfo, dpId, etreeLeafTag, etreeLeafGroupId); } } @SuppressWarnings("checkstyle:IllegalCatch") private void setBCGrouponOtherDpns(ElanInstance elanInfo, BigInteger dpId, int elanTag, long groupId) { int bucketId = 0; ElanDpnInterfacesList elanDpns = elanUtils.getElanDpnInterfacesList(elanInfo.getElanInstanceName()); if (elanDpns != null) { List<DpnInterfaces> dpnInterfaceses = elanDpns.getDpnInterfaces(); for (DpnInterfaces dpnInterface : dpnInterfaceses) { List<Bucket> remoteListBucketInfo = new ArrayList<>(); if (elanUtils.isDpnPresent(dpnInterface.getDpId()) && !Objects.equals(dpnInterface.getDpId(), dpId) && dpnInterface.getInterfaces() != null && !dpnInterface.getInterfaces().isEmpty()) { List<Action> listAction = new ArrayList<>(); int actionKey = 0; listAction.add(new ActionGroup(ElanUtils.getElanLocalBCGId(elanTag)).buildAction(++actionKey)); remoteListBucketInfo.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; for (DpnInterfaces otherFes : dpnInterfaceses) { if (elanUtils.isDpnPresent(otherFes.getDpId()) && !Objects.equals(otherFes.getDpId(), dpnInterface.getDpId()) && otherFes.getInterfaces() != null && !otherFes.getInterfaces().isEmpty()) { try { List<Action> remoteListActionInfo = elanUtils.getInternalTunnelItmEgressAction( dpnInterface.getDpId(), otherFes.getDpId(), elanTag); if (!remoteListActionInfo.isEmpty()) { remoteListBucketInfo .add(MDSALUtil.buildBucket(remoteListActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; } } catch (Exception ex) { LOG.error( "setElanBCGrouponOtherDpns failed due to Exception caught; " + "Logical Group Interface not found between source Dpn - {}, " + "destination Dpn - {} ", dpnInterface.getDpId(), otherFes.getDpId(), ex); return; } } } List<Bucket> elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpId, bucketId); remoteListBucketInfo.addAll(elanL2GwDevicesBuckets); if (remoteListBucketInfo.size() == 0) { LOG.debug("No ITM is present on Dpn - {} ", dpnInterface.getDpId()); continue; } Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(remoteListBucketInfo)); LOG.trace("Installing remote bc group {} on dpnId {}", group, dpnInterface.getDpId()); mdsalManager.syncInstallGroup(dpnInterface.getDpId(), group, ElanConstants.DELAY_TIME_IN_MILLISECOND); } } try { Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL); } catch (InterruptedException e1) { LOG.warn("Error while waiting for remote BC group on other DPNs for ELAN {} to install", elanInfo); } } } /** * Returns the bucket info with the given interface as the only bucket. */ private Bucket getLocalBCGroupBucketInfo(InterfaceInfo interfaceInfo, int bucketIdStart) { return MDSALUtil.buildBucket(getInterfacePortActions(interfaceInfo), MDSALUtil.GROUP_WEIGHT, bucketIdStart, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP); } private List<MatchInfo> buildMatchesForVni(Long vni) { List<MatchInfo> mkMatches = new ArrayList<>(); MatchInfo match = new MatchTunnelId(BigInteger.valueOf(vni)); mkMatches.add(match); return mkMatches; } private List<Instruction> getInstructionsForOutGroup(long groupId) { List<Instruction> mkInstructions = new ArrayList<>(); List<Action> actions = new ArrayList<>(); actions.add(new ActionGroup(groupId).buildAction()); mkInstructions.add(MDSALUtil.getWriteActionsInstruction(actions, 0)); return mkInstructions; } private List<MatchInfo> getMatchesForElanTag(long elanTag, boolean isSHFlagSet) { List<MatchInfo> mkMatches = new ArrayList<>(); // Matching metadata mkMatches.add(new MatchMetadata(ElanUtils.getElanMetadataLabel(elanTag, isSHFlagSet), MetaDataUtil.METADATA_MASK_SERVICE_SH_FLAG)); return mkMatches; } /** * Builds the list of instructions to be installed in the External Tunnel * table (38), which so far consists in writing the elanTag in metadata and * send packet to the new DHCP table. * * @param elanTag * elanTag to be written in metadata when flow is selected * @return the instructions ready to be installed in a flow */ private List<InstructionInfo> getInstructionsExtTunnelTable(Long elanTag) { List<InstructionInfo> mkInstructions = new ArrayList<>(); mkInstructions.add(new InstructionWriteMetadata(ElanUtils.getElanMetadataLabel(elanTag), ElanUtils.getElanMetadataMask())); // TODO: We should point to SMAC or DMAC depending on a configuration property to enable MAC learning mkInstructions.add(new InstructionGotoTable(NwConstants.ELAN_DMAC_TABLE)); return mkInstructions; } // Install DMAC entry on dst DPN public void installDMacAddressTables(ElanInstance elanInfo, InterfaceInfo interfaceInfo, BigInteger dstDpId) throws ElanException { String interfaceName = interfaceInfo.getInterfaceName(); ElanInterfaceMac elanInterfaceMac = elanUtils.getElanInterfaceMacByInterfaceName(interfaceName); if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) { List<MacEntry> macEntries = elanInterfaceMac.getMacEntry(); WriteTransaction writeFlowTx = broker.newWriteOnlyTransaction(); for (MacEntry macEntry : macEntries) { String macAddress = macEntry.getMacAddress().getValue(); LOG.info("Installing remote dmac for mac address {} and interface {}", macAddress, interfaceName); synchronized (ElanUtils.getElanMacDPNKey(elanInfo.getElanTag(), macAddress, interfaceInfo.getDpId())) { LOG.info("Acquired lock for mac : " + macAddress + ". Proceeding with remote dmac" + " install operation."); elanUtils.setupDMacFlowonRemoteDpn(elanInfo, interfaceInfo, dstDpId, macAddress, writeFlowTx); } } writeFlowTx.submit(); } } public void setupElanBroadcastGroups(ElanInstance elanInfo, BigInteger dpnId) { setupElanBroadcastGroups(elanInfo, null, dpnId); } public void setupElanBroadcastGroups(ElanInstance elanInfo, DpnInterfaces dpnInterfaces, BigInteger dpnId) { setupStandardElanBroadcastGroups(elanInfo, dpnInterfaces, dpnId); setupLeavesEtreeBroadcastGroups(elanInfo, dpnInterfaces, dpnId); } public void setupStandardElanBroadcastGroups(ElanInstance elanInfo, DpnInterfaces dpnInterfaces, BigInteger dpnId) { List<Bucket> listBucket = new ArrayList<>(); int bucketId = 0; int actionKey = 0; Long elanTag = elanInfo.getElanTag(); List<Action> listAction = new ArrayList<>(); listAction.add(new ActionGroup(ElanUtils.getElanLocalBCGId(elanTag)).buildAction(++actionKey)); listBucket.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; List<Bucket> listBucketInfoRemote = getRemoteBCGroupBuckets(elanInfo, dpnInterfaces, dpnId, bucketId, elanInfo.getElanTag()); listBucket.addAll(listBucketInfoRemote); long groupId = ElanUtils.getElanRemoteBCGId(elanTag); Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBucket)); LOG.trace("Installing the remote BroadCast Group:{}", group); mdsalManager.syncInstallGroup(dpnId, group, ElanConstants.DELAY_TIME_IN_MILLISECOND); } public void setupLeavesEtreeBroadcastGroups(ElanInstance elanInfo, DpnInterfaces dpnInterfaces, BigInteger dpnId) { EtreeInstance etreeInstance = elanInfo.getAugmentation(EtreeInstance.class); if (etreeInstance != null) { long etreeLeafTag = etreeInstance.getEtreeLeafTagVal().getValue(); List<Bucket> listBucket = new ArrayList<>(); int bucketId = 0; int actionKey = 0; List<Action> listAction = new ArrayList<>(); listAction .add(new ActionGroup(ElanUtils.getEtreeLeafLocalBCGId(etreeLeafTag)).buildAction(++actionKey)); listBucket.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; List<Bucket> listBucketInfoRemote = getRemoteBCGroupBuckets(elanInfo, dpnInterfaces, dpnId, bucketId, etreeLeafTag); listBucket.addAll(listBucketInfoRemote); long groupId = ElanUtils.getEtreeLeafRemoteBCGId(etreeLeafTag); Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBucket)); LOG.trace("Installing the remote BroadCast Group:{}", group); mdsalManager.syncInstallGroup(dpnId, group, ElanConstants.DELAY_TIME_IN_MILLISECOND); } } private void createDropBucket(List<Bucket> listBucket) { List<Action> actionsInfos = new ArrayList<>(); actionsInfos.add(new ActionDrop().buildAction()); Bucket dropBucket = MDSALUtil.buildBucket(actionsInfos, MDSALUtil.GROUP_WEIGHT, 0, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP); listBucket.add(dropBucket); } public void setupLocalBroadcastGroups(ElanInstance elanInfo, DpnInterfaces newDpnInterface, InterfaceInfo interfaceInfo) { setupStandardLocalBroadcastGroups(elanInfo, newDpnInterface, interfaceInfo); setupLeavesLocalBroadcastGroups(elanInfo, newDpnInterface, interfaceInfo); } public void setupStandardLocalBroadcastGroups(ElanInstance elanInfo, DpnInterfaces newDpnInterface, InterfaceInfo interfaceInfo) { List<Bucket> listBucket = new ArrayList<>(); int bucketId = 0; long groupId = ElanUtils.getElanLocalBCGId(elanInfo.getElanTag()); List<String> interfaces = new ArrayList<>(); if (newDpnInterface != null) { interfaces = newDpnInterface.getInterfaces(); } for (String ifName : interfaces) { // In case if there is a InterfacePort in the cache which is not in // operational state, skip processing it InterfaceInfo ifInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(ifName, interfaceInfo.getInterfaceType()); if (!isOperational(ifInfo)) { continue; } if (!interfaceManager.isExternalInterface(ifName)) { listBucket.add(MDSALUtil.buildBucket(getInterfacePortActions(ifInfo), MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; } } Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBucket)); LOG.trace("installing the localBroadCast Group:{}", group); mdsalManager.syncInstallGroup(interfaceInfo.getDpId(), group, ElanConstants.DELAY_TIME_IN_MILLISECOND); } private void setupLeavesLocalBroadcastGroups(ElanInstance elanInfo, DpnInterfaces newDpnInterface, InterfaceInfo interfaceInfo) { EtreeInstance etreeInstance = elanInfo.getAugmentation(EtreeInstance.class); if (etreeInstance != null) { List<Bucket> listBucket = new ArrayList<>(); int bucketId = 0; List<String> interfaces = new ArrayList<>(); if (newDpnInterface != null) { interfaces = newDpnInterface.getInterfaces(); } for (String ifName : interfaces) { // In case if there is a InterfacePort in the cache which is not // in // operational state, skip processing it InterfaceInfo ifInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(ifName, interfaceInfo.getInterfaceType()); if (!isOperational(ifInfo)) { continue; } if (!interfaceManager.isExternalInterface(ifName)) { // only add root interfaces bucketId = addInterfaceIfRootInterface(bucketId, ifName, listBucket, ifInfo); } } if (listBucket.size() == 0) { // No Buckets createDropBucket(listBucket); } long etreeLeafTag = etreeInstance.getEtreeLeafTagVal().getValue(); long groupId = ElanUtils.getEtreeLeafLocalBCGId(etreeLeafTag); Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBucket)); LOG.trace("installing the localBroadCast Group:{}", group); mdsalManager.syncInstallGroup(interfaceInfo.getDpId(), group, ElanConstants.DELAY_TIME_IN_MILLISECOND); } } private int addInterfaceIfRootInterface(int bucketId, String ifName, List<Bucket> listBucket, InterfaceInfo ifInfo) { EtreeInterface etreeInterface = ElanUtils.getEtreeInterfaceByElanInterfaceName(broker, ifName); if (etreeInterface != null && etreeInterface.getEtreeInterfaceType() == EtreeInterfaceType.Root) { listBucket.add(MDSALUtil.buildBucket(getInterfacePortActions(ifInfo), MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; } return bucketId; } public void removeLocalBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction deleteFlowGroupTx) { BigInteger dpnId = interfaceInfo.getDpId(); long groupId = ElanUtils.getElanLocalBCGId(elanInfo.getElanTag()); List<Bucket> listBuckets = new ArrayList<>(); int bucketId = 0; listBuckets.add(getLocalBCGroupBucketInfo(interfaceInfo, bucketId)); // listBuckets.addAll(getRemoteBCGroupBucketInfos(elanInfo, 1, // interfaceInfo)); Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBuckets)); LOG.trace("deleted the localBroadCast Group:{}", group); mdsalManager.removeGroupToTx(dpnId, group, deleteFlowGroupTx); } public void removeElanBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo, WriteTransaction deleteFlowGroupTx) { int bucketId = 0; int actionKey = 0; Long elanTag = elanInfo.getElanTag(); List<Bucket> listBuckets = new ArrayList<>(); List<Action> listAction = new ArrayList<>(); listAction.add(new ActionGroup(++actionKey, ElanUtils.getElanLocalBCGId(elanTag)).buildAction()); listBuckets.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; listBuckets.addAll(getRemoteBCGroupBucketInfos(elanInfo, bucketId, interfaceInfo, elanInfo.getElanTag())); BigInteger dpnId = interfaceInfo.getDpId(); long groupId = ElanUtils.getElanRemoteBCGId(elanInfo.getElanTag()); Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBuckets)); LOG.trace("deleting the remoteBroadCast group:{}", group); mdsalManager.removeGroupToTx(dpnId, group, deleteFlowGroupTx); } /** * Installs a flow in the External Tunnel table consisting in translating * the VNI retrieved from the packet that came over a tunnel with a TOR into * elanTag that will be used later in the ELANs pipeline. * * @param dpnId * the dpn id * @param elanInfo * the elan info */ public void setExternalTunnelTable(BigInteger dpnId, ElanInstance elanInfo) { long elanTag = elanInfo.getElanTag(); FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.EXTERNAL_TUNNEL_TABLE, getFlowRef(NwConstants.EXTERNAL_TUNNEL_TABLE, elanTag), 5, // prio elanInfo.getElanInstanceName(), // flowName 0, // idleTimeout 0, // hardTimeout ITMConstants.COOKIE_ITM_EXTERNAL.add(BigInteger.valueOf(elanTag)), buildMatchesForVni(ElanUtils.getVxlanSegmentationId(elanInfo)), getInstructionsExtTunnelTable(elanTag)); mdsalManager.installFlow(flowEntity); } /** * Removes, from External Tunnel table, the flow that translates from VNI to * elanTag. Important: ensure this method is only called whenever there is * no other ElanInterface in the specified DPN * * @param dpnId * DPN whose Ext Tunnel table is going to be modified * @param elanInfo * holds the elanTag needed for selecting the flow to be removed */ public void unsetExternalTunnelTable(BigInteger dpnId, ElanInstance elanInfo) { // TODO: Use DataStoreJobCoordinator in order to avoid that removing the // last ElanInstance plus // adding a new one does (almost at the same time) are executed in that // exact order String flowId = getFlowRef(NwConstants.EXTERNAL_TUNNEL_TABLE, elanInfo.getElanTag()); FlowEntity flowEntity = new FlowEntity(dpnId); flowEntity.setTableId(NwConstants.EXTERNAL_TUNNEL_TABLE); flowEntity.setFlowId(flowId); mdsalManager.removeFlow(flowEntity); } public void setupTerminateServiceTable(ElanInstance elanInfo, BigInteger dpId, WriteTransaction writeFlowGroupTx) { setupTerminateServiceTable(elanInfo, dpId, elanInfo.getElanTag(), writeFlowGroupTx); setupEtreeTerminateServiceTable(elanInfo, dpId, writeFlowGroupTx); } public void setupTerminateServiceTable(ElanInstance elanInfo, BigInteger dpId, long elanTag, WriteTransaction writeFlowGroupTx) { Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE, getFlowRef(NwConstants.INTERNAL_TUNNEL_TABLE, elanTag), 5, String.format("%s:%d", "ITM Flow Entry ", elanTag), 0, 0, ITMConstants.COOKIE_ITM.add(BigInteger.valueOf(elanTag)), ElanUtils.getTunnelMatchesForServiceId((int) elanTag), getInstructionsForOutGroup(ElanUtils.getElanLocalBCGId(elanTag))); mdsalManager.addFlowToTx(dpId, flowEntity, writeFlowGroupTx); } private void setupEtreeTerminateServiceTable(ElanInstance elanInfo, BigInteger dpId, WriteTransaction writeFlowGroupTx) { EtreeInstance etreeInstance = elanInfo.getAugmentation(EtreeInstance.class); if (etreeInstance != null) { setupTerminateServiceTable(elanInfo, dpId, etreeInstance.getEtreeLeafTagVal().getValue(), writeFlowGroupTx); } } public void setupUnknownDMacTable(ElanInstance elanInfo, BigInteger dpId, WriteTransaction writeFlowGroupTx) { long elanTag = elanInfo.getElanTag(); installLocalUnknownFlow(elanInfo, dpId, elanTag, writeFlowGroupTx); installRemoteUnknownFlow(elanInfo, dpId, elanTag, writeFlowGroupTx); setupEtreeUnknownDMacTable(elanInfo, dpId, elanTag, writeFlowGroupTx); } private void setupEtreeUnknownDMacTable(ElanInstance elanInfo, BigInteger dpId, long elanTag, WriteTransaction writeFlowGroupTx) { EtreeLeafTagName etreeLeafTag = elanUtils.getEtreeLeafTagByElanTag(elanTag); if (etreeLeafTag != null) { long leafTag = etreeLeafTag.getEtreeLeafTag().getValue(); installRemoteUnknownFlow(elanInfo, dpId, leafTag, writeFlowGroupTx); installLocalUnknownFlow(elanInfo, dpId, leafTag, writeFlowGroupTx); } } private void installLocalUnknownFlow(ElanInstance elanInfo, BigInteger dpId, long elanTag, WriteTransaction writeFlowGroupTx) { Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.ELAN_UNKNOWN_DMAC_TABLE, getUnknownDmacFlowRef(NwConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag, /* SH flag */false), 5, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_UNKNOWN_DMAC.add(BigInteger.valueOf(elanTag)), getMatchesForElanTag(elanTag, /* SH flag */false), getInstructionsForOutGroup(ElanUtils.getElanRemoteBCGId(elanTag))); mdsalManager.addFlowToTx(dpId, flowEntity, writeFlowGroupTx); } private void installRemoteUnknownFlow(ElanInstance elanInfo, BigInteger dpId, long elanTag, WriteTransaction writeFlowGroupTx) { // only if ELAN can connect to external network, perform the following if (ElanUtils.isVxlan(elanInfo) || ElanUtils.isVxlanSegment(elanInfo) || ElanUtils.isVlan(elanInfo) || ElanUtils.isFlat(elanInfo)) { Flow flowEntity2 = MDSALUtil.buildFlowNew(NwConstants.ELAN_UNKNOWN_DMAC_TABLE, getUnknownDmacFlowRef(NwConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag, /* SH flag */true), 5, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_UNKNOWN_DMAC.add(BigInteger.valueOf(elanTag)), getMatchesForElanTag(elanTag, /* SH flag */true), getInstructionsForOutGroup(ElanUtils.getElanLocalBCGId(elanTag))); mdsalManager.addFlowToTx(dpId, flowEntity2, writeFlowGroupTx); } } private void removeUnknownDmacFlow(BigInteger dpId, ElanInstance elanInfo, WriteTransaction deleteFlowGroupTx, long elanTag) { Flow flow = new FlowBuilder() .setId(new FlowId( getUnknownDmacFlowRef(NwConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag, /* SH flag */ false))) .setTableId(NwConstants.ELAN_UNKNOWN_DMAC_TABLE).build(); mdsalManager.removeFlowToTx(dpId, flow, deleteFlowGroupTx); if (ElanUtils.isVxlan(elanInfo) || ElanUtils.isVxlanSegment(elanInfo)) { Flow flow2 = new FlowBuilder().setId(new FlowId( getUnknownDmacFlowRef(NwConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag, /* SH flag */ true))) .setTableId(NwConstants.ELAN_UNKNOWN_DMAC_TABLE).build(); mdsalManager.removeFlowToTx(dpId, flow2, deleteFlowGroupTx); } } private void removeDefaultTermFlow(BigInteger dpId, long elanTag) { elanUtils.removeTerminatingServiceAction(dpId, (int) elanTag); } private void bindService(ElanInstance elanInfo, ElanInterface elanInterface, int lportTag, WriteTransaction tx) { if (isStandardElanService(elanInterface)) { bindElanService(elanInfo.getElanTag(), elanInfo.getElanInstanceName(), elanInterface.getName(), lportTag, tx); } else { // Etree service bindEtreeService(elanInfo, elanInterface, lportTag, tx); } } private void bindElanService(long elanTag, String elanInstanceName, String interfaceName, int lportTag, WriteTransaction tx) { int instructionKey = 0; List<Instruction> instructions = new ArrayList<>(); instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(ElanUtils.getElanMetadataLabel(elanTag), MetaDataUtil.METADATA_MASK_SERVICE, ++instructionKey)); List<Action> actions = new ArrayList<>(); actions.add(new ActionRegLoad(0, NxmNxReg1.class, 0, ElanConstants.INTERFACE_TAG_LENGTH - 1, lportTag) .buildAction()); actions.add(new ActionRegLoad(1, ElanConstants.ELAN_REG_ID, 0, ElanConstants.ELAN_TAG_LENGTH - 1, elanTag) .buildAction()); instructions.add(MDSALUtil.buildApplyActionsInstruction(actions, ++instructionKey)); instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.ELAN_BASE_TABLE, ++instructionKey)); short elanServiceIndex = ServiceIndex.getIndex(NwConstants.ELAN_SERVICE_NAME, NwConstants.ELAN_SERVICE_INDEX); BoundServices serviceInfo = ElanUtils.getBoundServices( String.format("%s.%s.%s", "elan", elanInstanceName, interfaceName), elanServiceIndex, ElanConstants.ELAN_SERVICE_PRIORITY, NwConstants.COOKIE_ELAN_INGRESS_TABLE, instructions); InstanceIdentifier<BoundServices> bindServiceId = ElanUtils.buildServiceId(interfaceName, elanServiceIndex); Optional<BoundServices> existingElanService = elanUtils.read(broker, LogicalDatastoreType.CONFIGURATION, bindServiceId); if (!existingElanService.isPresent()) { tx.put(LogicalDatastoreType.CONFIGURATION, ElanUtils.buildServiceId(interfaceName, elanServiceIndex), serviceInfo, true); } } private void bindEtreeService(ElanInstance elanInfo, ElanInterface elanInterface, int lportTag, WriteTransaction tx) { if (elanInterface.getAugmentation(EtreeInterface.class) .getEtreeInterfaceType() == EtreeInterfaceType.Root) { bindElanService(elanInfo.getElanTag(), elanInfo.getElanInstanceName(), elanInterface.getName(), lportTag, tx); } else { EtreeInstance etreeInstance = elanInfo.getAugmentation(EtreeInstance.class); if (etreeInstance == null) { LOG.error("EtreeInterface " + elanInterface.getName() + " is associated with a non EtreeInstance: " + elanInfo.getElanInstanceName()); } else { bindElanService(etreeInstance.getEtreeLeafTagVal().getValue(), elanInfo.getElanInstanceName(), elanInterface.getName(), lportTag, tx); } } } private boolean isStandardElanService(ElanInterface elanInterface) { return elanInterface.getAugmentation(EtreeInterface.class) == null; } private boolean isStandardElanService(ElanInstance elanInstance) { return elanInstance.getAugmentation(EtreeInstance.class) == null; } private void unbindService(ElanInstance elanInfo, String interfaceName, WriteTransaction tx) { short elanServiceIndex = ServiceIndex.getIndex(NwConstants.ELAN_SERVICE_NAME, NwConstants.ELAN_SERVICE_INDEX); InstanceIdentifier<BoundServices> bindServiceId = ElanUtils.buildServiceId(interfaceName, elanServiceIndex); Optional<BoundServices> existingElanService = elanUtils.read(broker, LogicalDatastoreType.CONFIGURATION, bindServiceId); if (existingElanService.isPresent()) { tx.delete(LogicalDatastoreType.CONFIGURATION, bindServiceId); } } private String getFlowRef(long tableId, long elanTag) { return new StringBuffer().append(tableId).append(elanTag).toString(); } private String getUnknownDmacFlowRef(long tableId, long elanTag, boolean shFlag) { return new StringBuffer().append(tableId).append(elanTag).append(shFlag).toString(); } private List<Action> getInterfacePortActions(InterfaceInfo interfaceInfo) { List<Action> listAction = new ArrayList<>(); int actionKey = 0; listAction.add(new ActionSetFieldTunnelId(BigInteger.valueOf(interfaceInfo.getInterfaceTag())) .buildAction(actionKey)); actionKey++; listAction.add(new ActionNxResubmit(NwConstants.ELAN_FILTER_EQUALS_TABLE).buildAction(actionKey)); return listAction; } private DpnInterfaces updateElanDpnInterfacesList(String elanInstanceName, BigInteger dpId, List<String> interfaceNames, WriteTransaction tx) { DpnInterfaces dpnInterface = new DpnInterfacesBuilder().setDpId(dpId).setInterfaces(interfaceNames) .setKey(new DpnInterfacesKey(dpId)).build(); tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId), dpnInterface, true); return dpnInterface; } /** * Delete elan dpn interface from operational DS. * * @param elanInstanceName * the elan instance name * @param dpId * the dp id */ private void deleteElanDpnInterface(String elanInstanceName, BigInteger dpId, WriteTransaction tx) { tx.delete(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId)); } private DpnInterfaces createElanInterfacesList(String elanInstanceName, String interfaceName, BigInteger dpId, WriteTransaction tx) { List<String> interfaceNames = new ArrayList<>(); interfaceNames.add(interfaceName); DpnInterfaces dpnInterface = new DpnInterfacesBuilder().setDpId(dpId).setInterfaces(interfaceNames) .setKey(new DpnInterfacesKey(dpId)).build(); tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId), dpnInterface, true); return dpnInterface; } private void createElanInterfaceTablesList(String interfaceName, WriteTransaction tx) { InstanceIdentifier<ElanInterfaceMac> elanInterfaceMacTables = ElanUtils .getElanInterfaceMacEntriesOperationalDataPath(interfaceName); Optional<ElanInterfaceMac> interfaceMacTables = elanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanInterfaceMacTables); // Adding new Elan Interface Port to the operational DataStore without // Static-Mac Entries.. if (!interfaceMacTables.isPresent()) { ElanInterfaceMac elanInterfaceMacTable = new ElanInterfaceMacBuilder().setElanInterface(interfaceName) .setKey(new ElanInterfaceMacKey(interfaceName)).build(); tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInterfaceMacEntriesOperationalDataPath(interfaceName), elanInterfaceMacTable, true); } } private void createElanStateList(String elanInstanceName, String interfaceName, WriteTransaction tx) { InstanceIdentifier<Elan> elanInstance = ElanUtils.getElanInstanceOperationalDataPath(elanInstanceName); Optional<Elan> elanInterfaceLists = elanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanInstance); // Adding new Elan Interface Port to the operational DataStore without // Static-Mac Entries.. if (elanInterfaceLists.isPresent()) { List<String> interfaceLists = elanInterfaceLists.get().getElanInterfaces(); if (interfaceLists == null) { interfaceLists = new ArrayList<>(); } interfaceLists.add(interfaceName); Elan elanState = new ElanBuilder().setName(elanInstanceName).setElanInterfaces(interfaceLists) .setKey(new ElanKey(elanInstanceName)).build(); tx.put(LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanInstanceName), elanState, true); } } private boolean isOperational(InterfaceInfo interfaceInfo) { if (interfaceInfo == null) { return false; } return interfaceInfo.getAdminState() == InterfaceInfo.InterfaceAdminState.ENABLED; } public void handleInternalTunnelStateEvent(BigInteger srcDpId, BigInteger dstDpId) throws ElanException { ElanDpnInterfaces dpnInterfaceLists = elanUtils.getElanDpnInterfacesList(); LOG.trace("processing tunnel state event for srcDpId {} dstDpId {}" + " and dpnInterfaceList {}", srcDpId, dstDpId, dpnInterfaceLists); if (dpnInterfaceLists == null) { return; } List<ElanDpnInterfacesList> elanDpnIf = dpnInterfaceLists.getElanDpnInterfacesList(); for (ElanDpnInterfacesList elanDpns : elanDpnIf) { int cnt = 0; String elanName = elanDpns.getElanInstanceName(); ElanInstance elanInfo = ElanUtils.getElanInstanceByName(broker, elanName); if (elanInfo == null) { LOG.warn("ELAN Info is null for elanName {} that does exist in elanDpnInterfaceList, " + "skipping this ELAN for tunnel handling", elanName); continue; } if (ElanUtils.isFlat(elanInfo) || ElanUtils.isVlan(elanInfo)) { LOG.debug("Ignoring internal tunnel state event for Flat/Vlan elan {}", elanName); continue; } List<DpnInterfaces> dpnInterfaces = elanDpns.getDpnInterfaces(); if (dpnInterfaces == null) { continue; } DpnInterfaces dstDpnIf = null; for (DpnInterfaces dpnIf : dpnInterfaces) { BigInteger dpnIfDpId = dpnIf.getDpId(); if (dpnIfDpId.equals(srcDpId)) { cnt++; } else if (dpnIfDpId.equals(dstDpId)) { cnt++; dstDpnIf = dpnIf; } } if (cnt == 2) { LOG.info("Elan instance:{} is present b/w srcDpn:{} and dstDpn:{}", elanName, srcDpId, dstDpId); DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance(); final DpnInterfaces finalDstDpnIf = dstDpnIf; // var needs to be final so it can be accessed in lambda dataStoreCoordinator.enqueueJob(elanName, () -> { // update Remote BC Group LOG.trace("procesing elan remote bc group for tunnel event {}", elanInfo); setupElanBroadcastGroups(elanInfo, srcDpId); Set<String> interfaceLists = new HashSet<>(); interfaceLists.addAll(finalDstDpnIf.getInterfaces()); for (String ifName : interfaceLists) { dataStoreCoordinator.enqueueJob(ifName, () -> { LOG.info("Processing tunnel up event for elan {} and interface {}", elanName, ifName); InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(ifName); if (isOperational(interfaceInfo)) { installDMacAddressTables(elanInfo, interfaceInfo, srcDpId); } return null; }); } return null; }); } } } /** * Handle external tunnel state event. * * @param externalTunnel * the external tunnel * @param intrf * the interface * @throws ElanException in case of issues creating the flow objects */ public void handleExternalTunnelStateEvent(ExternalTunnel externalTunnel, Interface intrf) throws ElanException { if (!validateExternalTunnelStateEvent(externalTunnel, intrf)) { return; } // dpId/externalNodeId will be available either in source or destination // based on the tunnel end point BigInteger dpId = null; NodeId externalNodeId = null; if (StringUtils.isNumeric(externalTunnel.getSourceDevice())) { dpId = new BigInteger(externalTunnel.getSourceDevice()); externalNodeId = new NodeId(externalTunnel.getDestinationDevice()); } else if (StringUtils.isNumeric(externalTunnel.getDestinationDevice())) { dpId = new BigInteger(externalTunnel.getDestinationDevice()); externalNodeId = new NodeId(externalTunnel.getSourceDevice()); } if (dpId == null || externalNodeId == null) { LOG.error("Dp ID / externalNodeId not found in external tunnel {}", externalTunnel); return; } ElanDpnInterfaces dpnInterfaceLists = elanUtils.getElanDpnInterfacesList(); if (dpnInterfaceLists == null) { return; } List<ElanDpnInterfacesList> elanDpnIf = dpnInterfaceLists.getElanDpnInterfacesList(); for (ElanDpnInterfacesList elanDpns : elanDpnIf) { String elanName = elanDpns.getElanInstanceName(); ElanInstance elanInfo = ElanUtils.getElanInstanceByName(broker, elanName); DpnInterfaces dpnInterfaces = elanUtils.getElanInterfaceInfoByElanDpn(elanName, dpId); if (dpnInterfaces == null || dpnInterfaces.getInterfaces() == null || dpnInterfaces.getInterfaces().isEmpty()) { continue; } LOG.debug("Elan instance:{} is present in Dpn:{} ", elanName, dpId); setupElanBroadcastGroups(elanInfo, dpId); // install L2gwDevices local macs in dpn. elanL2GatewayUtils.installL2gwDeviceMacsInDpn(dpId, externalNodeId, elanInfo, intrf.getName()); // Install dpn macs on external device elanL2GatewayUtils.installDpnMacsInL2gwDevice(elanName, new HashSet<>(dpnInterfaces.getInterfaces()), dpId, externalNodeId); } LOG.info("Handled ExternalTunnelStateEvent for {}", externalTunnel); } /** * Validate external tunnel state event. * * @param externalTunnel * the external tunnel * @param intrf * the intrf * @return true, if successful */ private boolean validateExternalTunnelStateEvent(ExternalTunnel externalTunnel, Interface intrf) { if (intrf.getOperStatus() == Interface.OperStatus.Up) { String srcDevice = externalTunnel.getDestinationDevice(); String destDevice = externalTunnel.getSourceDevice(); ExternalTunnel otherEndPointExtTunnel = elanUtils.getExternalTunnel(srcDevice, destDevice, LogicalDatastoreType.CONFIGURATION); LOG.trace("Validating external tunnel state: src tunnel {}, dest tunnel {}", externalTunnel, otherEndPointExtTunnel); if (otherEndPointExtTunnel != null) { boolean otherEndPointInterfaceOperational = ElanUtils .isInterfaceOperational(otherEndPointExtTunnel.getTunnelInterfaceName(), broker); if (otherEndPointInterfaceOperational) { return true; } else { LOG.debug("Other end [{}] of the external tunnel is not yet UP for {}", otherEndPointExtTunnel.getTunnelInterfaceName(), externalTunnel); } } } return false; } private List<MatchInfo> getMatchesForFilterEqualsLPortTag(int lportTag) { List<MatchInfo> mkMatches = new ArrayList<>(); // Matching metadata mkMatches.add(new MatchMetadata(MetaDataUtil.getLportTagMetaData(lportTag), MetaDataUtil.METADATA_MASK_LPORT_TAG)); mkMatches.add(new MatchTunnelId(BigInteger.valueOf(lportTag))); return mkMatches; } private List<MatchInfo> getTunnelIdMatchForFilterEqualsLPortTag(int lportTag) { List<MatchInfo> mkMatches = new ArrayList<>(); // Matching metadata mkMatches.add(new MatchTunnelId(BigInteger.valueOf(lportTag))); return mkMatches; } public void updateRemoteBroadcastGroupForAllElanDpns(ElanInstance elanInfo) { List<DpnInterfaces> dpns = elanUtils.getInvolvedDpnsInElan(elanInfo.getElanInstanceName()); if (dpns == null) { return; } for (DpnInterfaces dpn : dpns) { setupElanBroadcastGroups(elanInfo, dpn.getDpId()); } } public List<Bucket> getRemoteBCGroupBucketsOfElanL2GwDevices(ElanInstance elanInfo, BigInteger dpnId, int bucketId) { List<Bucket> listBucketInfo = new ArrayList<>(); ConcurrentMap<String, L2GatewayDevice> map = ElanL2GwCacheUtils .getInvolvedL2GwDevices(elanInfo.getElanInstanceName()); for (L2GatewayDevice device : map.values()) { String interfaceName = elanL2GatewayUtils.getExternalTunnelInterfaceName(String.valueOf(dpnId), device.getHwvtepNodeId()); if (interfaceName == null) { continue; } List<Action> listActionInfo = elanUtils.buildTunnelItmEgressActions(interfaceName, ElanUtils.getVxlanSegmentationId(elanInfo)); listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); bucketId++; } return listBucketInfo; } @Override protected ElanInterfaceManager getDataTreeChangeListener() { return this; } }