Use documenting constants for put()
[netvirt.git] / vpnservice / elanmanager / elanmanager-impl / src / main / java / org / opendaylight / netvirt / elan / internal / ElanPacketInHandler.java
index 8754656d5e04b5c969dc229e42d19769b6465c66..7f9a3a238ad803b807332d0f21112e08c90e5c9a 100755 (executable)
@@ -8,21 +8,25 @@
 package org.opendaylight.netvirt.elan.internal;
 
 import com.google.common.base.Optional;
+
 import java.math.BigInteger;
-import java.util.Arrays;
+import java.util.Collections;
 import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.PacketException;
 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.DataStoreJobCoordinator;
 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
-import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
 import org.opendaylight.genius.mdsalutil.NWUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
+import org.opendaylight.netvirt.elan.evpn.utils.EvpnUtils;
 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
 import org.opendaylight.netvirt.elan.utils.ElanUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
 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.genius.interfacemanager.meta.rev160406._if.indexes._interface.map.IfIndexInterface;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
@@ -41,18 +45,21 @@ import org.slf4j.LoggerFactory;
 @SuppressWarnings("deprecation")
 public class ElanPacketInHandler implements PacketProcessingListener {
 
-    private static final Logger logger = LoggerFactory.getLogger(ElanPacketInHandler.class);
+    private static final Logger LOG = LoggerFactory.getLogger(ElanPacketInHandler.class);
 
     private final DataBroker broker;
     private final IInterfaceManager interfaceManager;
     private final ElanUtils elanUtils;
     private final ElanL2GatewayUtils elanL2GatewayUtils;
+    private final EvpnUtils evpnUtils;
 
-    public ElanPacketInHandler(DataBroker dataBroker, final IInterfaceManager interfaceManager, ElanUtils elanUtils) {
+    public ElanPacketInHandler(DataBroker dataBroker, final IInterfaceManager interfaceManager, ElanUtils elanUtils,
+                               EvpnUtils evpnUtils) {
         broker = dataBroker;
         this.interfaceManager = interfaceManager;
         this.elanUtils = elanUtils;
         this.elanL2GatewayUtils = elanUtils.getElanL2GatewayUtils();
+        this.evpnUtils = evpnUtils;
     }
 
     @Override
@@ -60,6 +67,7 @@ public class ElanPacketInHandler implements PacketProcessingListener {
         Class<? extends PacketInReason> pktInReason = notification.getPacketInReason();
         short tableId = notification.getTableId().getValue();
         if (pktInReason == NoMatch.class && tableId == NwConstants.ELAN_SMAC_TABLE) {
+            ElanManagerCounters.unknown_smac_pktin_rcv.inc();
             try {
                 byte[] data = notification.getPayload();
                 Ethernet res = new Ethernet();
@@ -67,95 +75,140 @@ public class ElanPacketInHandler implements PacketProcessingListener {
                 res.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
 
                 byte[] srcMac = res.getSourceMACAddress();
-                String macAddress = NWUtil.toStringMacAddress(srcMac);
-                PhysAddress physAddress = new PhysAddress(macAddress);
-                BigInteger metadata = notification.getMatch().getMetadata().getMetadata();
-                long elanTag = MetaDataUtil.getElanTagFromMetadata(metadata);
+                final String macAddress = NWUtil.toStringMacAddress(srcMac);
+                final BigInteger metadata = notification.getMatch().getMetadata().getMetadata();
+                final long elanTag = MetaDataUtil.getElanTagFromMetadata(metadata);
 
                 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
 
                 Optional<IfIndexInterface> interfaceInfoOp = elanUtils.getInterfaceInfoByInterfaceTag(portTag);
                 if (!interfaceInfoOp.isPresent()) {
-                    logger.warn("There is no interface for given portTag {}", portTag);
+                    LOG.warn("There is no interface for given portTag {}", portTag);
                     return;
                 }
                 String interfaceName = interfaceInfoOp.get().getInterfaceName();
-                logger.debug("Received a packet with srcMac: {} ElanTag: {} PortTag: {} InterfaceName: {}", macAddress,
+                LOG.debug("Received a packet with srcMac: {} ElanTag: {} PortTag: {} InterfaceName: {}", macAddress,
                         elanTag, portTag, interfaceName);
                 ElanTagName elanTagName = elanUtils.getElanInfoByElanTag(elanTag);
                 if (elanTagName == null) {
-                    logger.warn("not able to find elanTagName in elan-tag-name-map for elan tag {}", elanTag);
+                    LOG.warn("not able to find elanTagName in elan-tag-name-map for elan tag {}", elanTag);
                     return;
                 }
                 String elanName = elanTagName.getName();
-                MacEntry macEntry = elanUtils.getInterfaceMacEntriesOperationalDataPath(interfaceName, physAddress);
-                if (macEntry != null && macEntry.getInterface() == interfaceName) {
-                    BigInteger macTimeStamp = macEntry.getControllerLearnedForwardingEntryTimestamp();
-                    if (System.currentTimeMillis() > macTimeStamp.longValue() + 2000) {
-                        /*
-                         * Protection time expired. Even though the MAC has been
-                         * learnt (it is in the cache) the packets are punted to
-                         * controller. Which means, the the flows were not
-                         * successfully created in the DPN, but the MAC entry
-                         * has been added successfully in the cache.
-                         *
-                         * So, the cache has to be cleared and the flows and
-                         * cache should be recreated (clearing of cache is
-                         * required so that the timestamp is updated).
-                         */
-                        InstanceIdentifier<MacEntry> macEntryId = ElanUtils
-                                .getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
-                        ElanUtils.delete(broker, LogicalDatastoreType.OPERATIONAL, macEntryId);
-                    } else {
-                        // Protection time running. Ignore packets for 2 seconds
-                        return;
-                    }
-                } else if (macEntry != null) {
-                    // MAC address has moved. Overwrite the mapping and replace
-                    // MAC flows
-                    long macTimeStamp = macEntry.getControllerLearnedForwardingEntryTimestamp().longValue();
-                    if (System.currentTimeMillis() > macTimeStamp + 1000) {
-
-                        InstanceIdentifier<MacEntry> macEntryId = ElanUtils
-                                .getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
-                        ElanUtils.delete(broker, LogicalDatastoreType.OPERATIONAL, macEntryId);
-                        tryAndRemoveInvalidMacEntry(elanName, macEntry);
-                    } else {
-                        // New FEs flood their packets on all interfaces. This
-                        // can lead
-                        // to many contradicting packet_ins. Ignore all packets
-                        // received
-                        // within 1s after the first packet_in
-                        return;
-                    }
+                PhysAddress physAddress = new PhysAddress(macAddress);
+                MacEntry oldMacEntry = elanUtils.getMacEntryForElanInstance(elanName, physAddress).orNull();
+                boolean isVlanOrFlatProviderIface = interfaceManager.isExternalInterface(interfaceName);
+
+                Optional<IpAddress> srcIpAddress = elanUtils.getSourceIpAddress(res);
+                MacEntry newMacEntry = null;
+                BigInteger timeStamp = new BigInteger(String.valueOf(System.currentTimeMillis()));
+                if (!srcIpAddress.isPresent()) {
+                    newMacEntry = new MacEntryBuilder().setInterface(interfaceName).setMacAddress(physAddress)
+                            .setKey(new MacEntryKey(physAddress))
+                            .setControllerLearnedForwardingEntryTimestamp(timeStamp)
+                            .setIsStaticAddress(false).build();
+                } else {
+                    newMacEntry = new MacEntryBuilder().setInterface(interfaceName).setMacAddress(physAddress)
+                            .setIpPrefix(srcIpAddress.get()).setKey(new MacEntryKey(physAddress))
+                            .setControllerLearnedForwardingEntryTimestamp(timeStamp)
+                            .setIsStaticAddress(false).build();
                 }
-                BigInteger timeStamp = new BigInteger(String.valueOf((long) System.currentTimeMillis()));
-                macEntry = new MacEntryBuilder().setInterface(interfaceName).setMacAddress(physAddress)
-                        .setKey(new MacEntryKey(physAddress)).setControllerLearnedForwardingEntryTimestamp(timeStamp)
-                        .setIsStaticAddress(false).build();
-                InstanceIdentifier<MacEntry> macEntryId = ElanUtils
-                        .getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
-                MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, macEntryId,
-                        macEntry);
-                InstanceIdentifier<MacEntry> elanMacEntryId = ElanUtils.getMacEntryOperationalDataPath(elanName,
-                        physAddress);
-                MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, elanMacEntryId,
-                        macEntry);
-                ElanInstance elanInstance = elanUtils.getElanInstanceByName(elanName);
-                WriteTransaction flowWritetx = broker.newWriteOnlyTransaction();
-                elanUtils.setupMacFlows(elanInstance,
-                        interfaceManager.getInterfaceInfo(interfaceName),
-                        elanInstance.getMacTimeout(), macAddress, flowWritetx);
-                flowWritetx.submit();
-
-                BigInteger dpId = interfaceManager.getDpnForInterface(interfaceName);
-                elanL2GatewayUtils.scheduleAddDpnMacInExtDevices(elanInstance.getElanInstanceName(), dpId,
-                        Arrays.asList(physAddress));
-            } catch (Exception e) {
-                logger.trace("Failed to decode packet: {}", e);
+                if (srcIpAddress.isPresent()) {
+                    String prefix = srcIpAddress.get().getIpv4Address().getValue();
+                    InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
+                    ElanInstance elanInstance = elanUtils.getElanInstanceByName(broker, elanName);
+                    evpnUtils.advertisePrefix(elanInstance, macAddress, prefix, interfaceName, interfaceInfo.getDpId());
+                }
+                final DataStoreJobCoordinator portDataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                enqueueJobForMacSpecificTasks(macAddress, elanTag, interfaceName, elanName, physAddress, oldMacEntry,
+                        newMacEntry, isVlanOrFlatProviderIface, portDataStoreCoordinator);
+
+                ElanInstance elanInstance = ElanUtils.getElanInstanceByName(broker, elanName);
+                InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
+                if (interfaceInfo == null) {
+                    LOG.trace("Interface:{} is not present under Config DS", interfaceName);
+                    return;
+                }
+                enqueueJobForDPNSpecificTasks(macAddress, elanTag, interfaceName, physAddress, elanInstance,
+                        interfaceInfo, oldMacEntry, newMacEntry, isVlanOrFlatProviderIface, portDataStoreCoordinator);
+
+
+            } catch (PacketException e) {
+                LOG.error("Failed to decode packet: {}", notification, e);
             }
         }
+    }
 
+    private void enqueueJobForMacSpecificTasks(final String macAddress, final long elanTag, String interfaceName,
+                                               String elanName, PhysAddress physAddress,
+                                               MacEntry oldMacEntry, MacEntry newMacEntry,
+                                               final boolean isVlanOrFlatProviderIface,
+                                               final DataStoreJobCoordinator portDataStoreCoordinator) {
+        portDataStoreCoordinator.enqueueJob(ElanUtils.getElanMacKey(elanTag, macAddress), () -> {
+            WriteTransaction writeTx = broker.newWriteOnlyTransaction();
+            if (oldMacEntry != null && oldMacEntry.getInterface().equals(interfaceName)) {
+                // This should never occur because of ovs temporary mac learning
+                ElanManagerCounters.unknown_smac_pktin_forwarding_entries_removed.inc();
+            } else if (oldMacEntry != null && !isVlanOrFlatProviderIface) {
+                long macTimeStamp = oldMacEntry.getControllerLearnedForwardingEntryTimestamp().longValue();
+                if (System.currentTimeMillis() > macTimeStamp + 1000) {
+                    InstanceIdentifier<MacEntry> macEntryId = ElanUtils
+                            .getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName,
+                                    physAddress);
+                    writeTx.delete(LogicalDatastoreType.OPERATIONAL, macEntryId);
+                } else {
+                    // New FEs flood their packets on all interfaces. This
+                    // can lead
+                    // to many contradicting packet_ins. Ignore all packets
+                    // received
+                    // within 1s after the first packet_in
+                    ElanManagerCounters.unknown_smac_pktin_mac_migration_ignored_due_to_protection.inc();
+                }
+            } else if (oldMacEntry != null) {
+                ElanManagerCounters.unknown_smac_pktin_removed_for_relearned.inc();
+            }
+            // This check is required only to update elan-forwarding-tables when mac is learned
+            // in ports (example: VM interfaces) other than on vlan provider port.
+            if (!isVlanOrFlatProviderIface && oldMacEntry == null) {
+                InstanceIdentifier<MacEntry> elanMacEntryId =
+                        ElanUtils.getMacEntryOperationalDataPath(elanName, physAddress);
+                writeTx.put(LogicalDatastoreType.OPERATIONAL, elanMacEntryId, newMacEntry,
+                        WriteTransaction.CREATE_MISSING_PARENTS);
+            }
+            return Collections.singletonList(writeTx.submit());
+        });
+    }
+
+    private void enqueueJobForDPNSpecificTasks(final String macAddress, final long elanTag, String interfaceName,
+                                               PhysAddress physAddress, ElanInstance elanInstance,
+                                               InterfaceInfo interfaceInfo, MacEntry oldMacEntry,
+                                               MacEntry newMacEntry, boolean isVlanOrFlatProviderIface,
+                                               final DataStoreJobCoordinator portDataStoreCoordinator) {
+        portDataStoreCoordinator
+                .enqueueJob(ElanUtils.getElanMacDPNKey(elanTag, macAddress, interfaceInfo.getDpId()), () -> {
+                    macMigrationFlowsCleanup(interfaceName, elanInstance, oldMacEntry, isVlanOrFlatProviderIface);
+                    BigInteger dpId = interfaceManager.getDpnForInterface(interfaceName);
+                    elanL2GatewayUtils.scheduleAddDpnMacInExtDevices(elanInstance.getElanInstanceName(), dpId,
+                            Collections.singletonList(physAddress));
+                    ElanManagerCounters.unknown_smac_pktin_learned.inc();
+                    WriteTransaction flowWritetx = broker.newWriteOnlyTransaction();
+                    elanUtils.setupMacFlows(elanInstance, interfaceInfo, elanInstance.getMacTimeout(),
+                            macAddress, !isVlanOrFlatProviderIface, flowWritetx);
+                    InstanceIdentifier<MacEntry> macEntryId =
+                            ElanUtils.getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
+                    flowWritetx.put(LogicalDatastoreType.OPERATIONAL, macEntryId, newMacEntry,
+                            WriteTransaction.CREATE_MISSING_PARENTS);
+                    return Collections.singletonList(flowWritetx.submit());
+                });
+    }
+
+    private void macMigrationFlowsCleanup(String interfaceName, ElanInstance elanInstance, MacEntry macEntry,
+                                          boolean isVlanOrFlatProviderIface) {
+        if (macEntry != null && !macEntry.getInterface().equals(interfaceName)
+                && !isVlanOrFlatProviderIface) {
+            tryAndRemoveInvalidMacEntry(elanInstance.getElanInstanceName(), macEntry);
+            ElanManagerCounters.unknown_smac_pktin_flows_removed_for_relearned.inc();
+        }
     }
 
     /*
@@ -164,26 +217,25 @@ public class ElanPacketInHandler implements PacketProcessingListener {
      * Static MAC having been added on a wrong ELAN.
      */
     private void tryAndRemoveInvalidMacEntry(String elanName, MacEntry macEntry) {
-        ElanInstance elanInfo = elanUtils.getElanInstanceByName(elanName);
+        ElanInstance elanInfo = ElanUtils.getElanInstanceByName(broker, elanName);
         if (elanInfo == null) {
-            logger.warn(String.format("MAC %s is been added (either statically or dynamically) for an invalid Elan %s. "
-                    + "Manual cleanup may be necessary", macEntry.getMacAddress(), elanName));
+            LOG.warn("MAC {} is been added (either statically or dynamically) for an invalid Elan {}. "
+                    + "Manual cleanup may be necessary", macEntry.getMacAddress(), elanName);
             return;
         }
 
         InterfaceInfo oldInterfaceLport = interfaceManager.getInterfaceInfo(macEntry.getInterface());
         if (oldInterfaceLport == null) {
-            logger.warn(
-                    String.format(
-                            "MAC %s is been added (either statically or dynamically) on an invalid Logical Port %s. "
-                                    + "Manual cleanup may be necessary",
-                            macEntry.getMacAddress(), macEntry.getInterface()));
+            LOG.warn("MAC {} is been added (either statically or dynamically) on an invalid Logical Port {}. "
+                            + "Manual cleanup may be necessary",
+                    macEntry.getMacAddress(), macEntry.getInterface());
             return;
         }
         WriteTransaction flowDeletetx = broker.newWriteOnlyTransaction();
         elanUtils.deleteMacFlows(elanInfo, oldInterfaceLport, macEntry, flowDeletetx);
         flowDeletetx.submit();
-        elanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo, Arrays.asList(macEntry.getMacAddress()));
+        elanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo,
+                Collections.singletonList(macEntry.getMacAddress()));
     }
 
 }