L2 Gw connection support and Elan manager changes 46/37246/3
authorShashidhar R <shashidhar.raja@ericsson.com>
Thu, 7 Apr 2016 10:07:17 +0000 (15:37 +0530)
committerShashidhar R <shashidhar.raja@ericsson.com>
Mon, 11 Apr 2016 08:48:07 +0000 (14:18 +0530)
- Handled L2 Gw Connection CRUD related to Logical switch creation/Ucast/Mcast table updates in elanmanager module.
- Updated to use VXLAN provider segmentation id as the ELAN VNI for SR-IOV use cases.

Change-Id: If7bcc0a45a51773a75c7b7117a6a89e713e7eddc
Signed-off-by: Shashidhar R <shashidhar.raja@ericsson.com>
37 files changed:
elanmanager/elanmanager-api/pom.xml
elanmanager/elanmanager-api/src/main/java/org/opendaylight/elanmanager/utils/ElanL2GwCacheUtils.java [new file with mode: 0644]
elanmanager/elanmanager-api/src/main/yang/elan.yang
elanmanager/elanmanager-impl/pom.xml
elanmanager/elanmanager-impl/src/main/config/default-config.xml
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/cli/l2gw/L2GwUtilsCacheCli.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanForwardingEntriesHandler.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInstanceManager.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceManager.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceStateChangeListener.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanNodeListener.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanPacketInHandler.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanServiceProvider.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanSmacFlowEventListener.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/internal/ElanL2GatewayProvider.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLocalUcastMacListener.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLogicalSwitchListener.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepNodeListener.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepRemoteMcastMacListener.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/L2GatewayConnectionListener.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayMulticastUtils.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayUtils.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/L2GatewayConnectionUtils.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitor.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitorMBean.java [new file with mode: 0644]
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanConstants.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanUtils.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModule.java
elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModuleFactory.java
elanmanager/elanmanager-impl/src/main/resources/OSGI-INF/blueprint/blueprint.xml
elanmanager/elanmanager-impl/src/main/yang/elanservice-impl.yang
mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepSouthboundUtils.java
mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepUtils.java
neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/L2GatewayDevice.java
neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronNetworkChangeListener.java
neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronPortChangeListener.java
neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronvpnUtils.java

index 1ff47063481b74a568ca97fac0973f6263b5da1e..99bd6d598005b1b902e3b8261b51e726bc04b1e7 100644 (file)
@@ -59,5 +59,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>config-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.vpnservice</groupId>
+      <artifactId>neutronvpn-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
   </dependencies>
 </project>
diff --git a/elanmanager/elanmanager-api/src/main/java/org/opendaylight/elanmanager/utils/ElanL2GwCacheUtils.java b/elanmanager/elanmanager-api/src/main/java/org/opendaylight/elanmanager/utils/ElanL2GwCacheUtils.java
new file mode 100644 (file)
index 0000000..4022cc2
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.elanmanager.utils;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.vpnservice.utils.cache.CacheUtil;
+
+public class ElanL2GwCacheUtils {
+    private static final ConcurrentHashMap<String, L2GatewayDevice> EMPTY_MAP = new ConcurrentHashMap<String, L2GatewayDevice>();
+    public static final String L2GATEWAY_CONN_CACHE_NAME = "L2GWCONN";
+
+    public static void createElanL2GwDeviceCache() {
+        if (CacheUtil.getCache(ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME) == null) {
+            CacheUtil.createCache(ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME);
+        }
+    }
+
+    public static void addL2GatewayDeviceToCache(String elanName, L2GatewayDevice l2GwDevice) {
+        ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>> cachedMap =
+                (ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>>) CacheUtil.getCache(
+                        ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME);
+        ConcurrentMap<String, L2GatewayDevice> deviceMap = cachedMap.get(elanName);
+        if (deviceMap == null) {
+            synchronized(ElanL2GwCacheUtils.class) {
+                deviceMap = cachedMap.get(elanName);
+                if (deviceMap == null) {
+                    deviceMap = new ConcurrentHashMap<String, L2GatewayDevice>();
+                    cachedMap.put(elanName, deviceMap);
+                }
+            }
+        }
+        deviceMap.put(l2GwDevice.getHwvtepNodeId(), l2GwDevice);
+    }
+
+    public static void removeL2GatewayDeviceFromAllElanCache(String deviceName) {
+        ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>> cachedMap =
+                (ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>>) CacheUtil.getCache(
+                        ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME);
+        for (String elanName : cachedMap.keySet()) {
+            ConcurrentMap<String, L2GatewayDevice> deviceMap = cachedMap.get(elanName);
+            if (deviceMap != null) {
+                deviceMap.remove(deviceName);
+            }
+        }
+    }
+
+
+    public static L2GatewayDevice removeL2GatewayDeviceFromCache(String elanName, String deviceName) {
+        ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>> cachedMap =
+                (ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>>) CacheUtil.getCache(
+                        ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME);
+        ConcurrentMap<String, L2GatewayDevice> deviceMap = cachedMap.get(elanName);
+        if (deviceMap != null) {
+            L2GatewayDevice device = deviceMap.remove(deviceName);
+            return device;
+        } else {
+            return null;
+        }
+    }
+
+    public static L2GatewayDevice getL2GatewayDeviceFromCache(String elanName, String deviceName) {
+        ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>> cachedMap =
+                (ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>>) CacheUtil.getCache(
+                        ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME);
+        ConcurrentMap<String, L2GatewayDevice> deviceMap = cachedMap.get(elanName);
+        if (deviceMap != null) {
+            return deviceMap.get(deviceName);
+        } else {
+            return null;
+        }
+    }
+
+    public static ConcurrentMap<String, L2GatewayDevice> getAllElanL2GatewayDevicesFromCache(String elanName) {
+        ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>> cachedMap = (ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>>) CacheUtil
+                .getCache(ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME);
+        ConcurrentMap<String, L2GatewayDevice> result = cachedMap.get(elanName);
+        if (result == null) {
+            result = EMPTY_MAP;
+        }
+        return result;
+    }
+
+    public static List<L2GatewayDevice> getAllElanDevicesFromCache() {
+        List<String> l2GwsList = new ArrayList<>();
+        ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>> cachedMap =
+                (ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>>) CacheUtil.getCache(
+                        ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME);
+        if (cachedMap == null || cachedMap.isEmpty()) {
+            return null;
+        }
+
+        List<L2GatewayDevice> l2GwDevices = new ArrayList<L2GatewayDevice>();
+        for (ConcurrentMap<String, L2GatewayDevice> l2gwDevices : cachedMap.values())
+        {
+            for (L2GatewayDevice l2gwDevice : l2gwDevices.values() ) {
+                l2GwDevices.add(l2gwDevice);
+            }
+        }
+
+        return l2GwDevices;
+    }
+
+}
index 5b4b105542a3786092c4940e982528a869f243b8..5ec20678067968af459c724155149ad9fa23e03e 100644 (file)
@@ -6,7 +6,10 @@ module elan {
     import ietf-interfaces {
         prefix if;
     }
-
+    import ietf-inet-types {
+        prefix inet;
+        revision-date "2010-09-24";
+    }
     import ietf-yang-types {
         prefix yang;
     }
@@ -16,47 +19,52 @@ module elan {
         description "elan module";
     }
 
-  /*
-   * elan instance view.
-   */
-  container elan-instances {
-    description
-      "elan instances configuration parameters.
-       elan instances support both the VLAN and VNI based elans.";
-
-    list elan-instance {
-        max-elements "unbounded";
-        min-elements "0";
-        key "elan-instance-name";
+    /*
+    * elan instance view.
+    */
+    container elan-instances {
         description
-            "Specifies the name of the elan instance. It is a string of 1 to 31
-             case-sensitive characters.";
-        leaf elan-instance-name {
-            type string;
+           "elan instances configuration parameters. Elan instances support both the VLAN and VNI based elans.";
+
+        list elan-instance {
+            max-elements "unbounded";
+            min-elements "0";
+            key "elan-instance-name";
             description
-              "The name of the elan-instance.";
-        }
-        leaf elan-tag {
-            type    uint32;
-            description "ELAN unique identifier which is unique across all the tenants. This will be created internally and if provided, the value will be discarded.";
-        }
-        leaf mac-timeout {
-            type uint32 {
-              range "0..65535";
+                "Specifies the name of the elan instance. It is a string of 1 to 31
+                 case-sensitive characters.";
+            leaf elan-instance-name {
+                type string;
+                description "The name of the elan-instance.";
             }
-            description "MAC Table entry ageing time in seconds. A value of 0 will indicate that the MAC will never expire.";
-        }
-        leaf description {
-            description
-                "A textual description of elan instance, the elan instance description
-                helps users memorize the elan instance.";
+            leaf elan-tag {
+                type    uint32;
+                description "ELAN unique identifier which is unique across all the tenants.
+                             This will be created internally and if provided, the value will be discarded.";
+            }
+            leaf vni {
+                type    uint32;
+                description "Optional. Network identifier. It's mandatory when there are external devices
+                             participating in the ELAN";
+            }
+            leaf mac-timeout {
+                type uint32 {
+                    range "0..65535";
+                }
+                description "MAC Table entry ageing time in seconds.
+                             A value of 0 will indicate that the MAC will never expire.";
+            }
+            leaf description {
+                description
+                    "A textual description of elan instance, the elan instance description
+                    helps users memorize the elan instance.";
 
-            type string {
-                length "1..254";
+                type string {
+                    length "1..254";
+                }
             }
         }
     }
-  }
 
   /*
    * Binding Interfaces to a elan Instance.
index 195226510442f9ff20cbf819664e7f2d8b2fa9ae..b7ea5772449886db8e910d4933e08c63d4fec1dd 100644 (file)
@@ -20,6 +20,12 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
   <artifactId>elanmanager-impl</artifactId>
   <version>${vpnservices.version}</version>
   <packaging>bundle</packaging>
+
+  <properties>
+    <powermock.version>1.6.4</powermock.version>
+    <mockitoall.version>1.10.19</mockitoall.version>
+  </properties>
+
   <dependencies>
     <dependency>
       <groupId>${project.groupId}</groupId>
@@ -46,10 +52,25 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <artifactId>itm-api</artifactId>
       <version>${vpnservices.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.vpnservice</groupId>
+      <artifactId>neutronvpn-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal-binding-broker-impl</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.ovsdb</groupId>
+      <artifactId>hwvtepsouthbound-api</artifactId>
+      <version>${vpns.ovsdb.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.neutron</groupId>
+      <artifactId>model</artifactId>
+      <version>${neutron.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.apache.karaf.shell</groupId>
       <artifactId>org.apache.karaf.shell.console</artifactId>
@@ -60,6 +81,31 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <artifactId>interfacemgr-impl</artifactId>
       <version>${vpnservices.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.vpnservice</groupId>
+      <artifactId>dhcpservice-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+
+      <!--  Only for unit-test -->
+   <dependency>
+        <groupId>org.mockito</groupId>
+        <artifactId>mockito-all</artifactId>
+        <version>${mockitoall.version}</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.powermock</groupId>
+        <artifactId>powermock-api-mockito</artifactId>
+        <version>${powermock.version}</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.powermock</groupId>
+        <artifactId>powermock-module-junit4</artifactId>
+        <version>${powermock.version}</version>
+        <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>
index f4f499163110e6ea1a44cd66201298c155da7606..b34398e2f8d1987d0d41a6b4f9c83fe953e88d70 100644 (file)
@@ -13,6 +13,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&amp;revision=2013-10-28</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:mdsalutil:api?module=odl-mdsalutil&amp;revision=2015-04-10</capability>
+        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:distributed-entity-ownership-service?module=distributed-entity-ownership-service&amp;revision=2015-08-10</capability>
         <capability>urn:opendaylight:vpnservice:itm?module=itm&amp;revision=2015-07-01</capability>
         <capability>urn:opendaylight:vpnservice:interfacemgr?module=odl-interface&amp;revision=2015-03-31</capability>
     </required-capabilities>
@@ -47,6 +48,14 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
                         <type xmlns:bindingimpl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">bindingimpl:binding-new-notification-service</type>
                         <name>binding-notification-adapter</name>
                     </notification-service>
+                    <entity-ownership-service>
+                        <type xmlns:entity-ownership="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:entity-ownership-service">entity-ownership:entity-ownership-service</type>
+                        <name>entity-ownership-service</name>
+                    </entity-ownership-service>
+                    <binding-normalized-node-serializer>
+                        <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-normalized-node-serializer</type>
+                        <name>runtime-mapping-singleton</name>
+                    </binding-normalized-node-serializer>
                 </module>
             </modules>
             <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/cli/l2gw/L2GwUtilsCacheCli.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/cli/l2gw/L2GwUtilsCacheCli.java
new file mode 100644 (file)
index 0000000..f995404
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.vpnservice.elan.cli.l2gw;
+
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils;
+import org.opendaylight.vpnservice.utils.cache.CacheUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Command(scope = "l2gw", name = "show-cache", description = "display l2gateways cache")
+public class L2GwUtilsCacheCli extends OsgiCommandSupport {
+    private static final Logger logger = LoggerFactory.getLogger(L2GwUtilsCacheCli.class);
+
+    private static final String DEMARCATION = "=================================";
+
+    @Option(name = "-cache", aliases = {"--cache"}, description = "cache name",
+            required = false, multiValued = false)
+    String cacheName = null;
+
+    @Option(name = "-elan", aliases = {"--elan"}, description = "elan name",
+            required = false, multiValued = false)
+    String elanName;
+
+    @Override
+    protected Object doExecute() {
+        try {
+            if (cacheName == null) {
+                System.out.println("Available caches");
+                System.out.println(ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME);
+                System.out.println(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME);
+                return null;
+            }
+            switch (cacheName) {
+            case ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME:
+                dumpElanL2GwCache();
+                break;
+            case L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME:
+                dumpL2GwCache();
+                break;
+            }
+        } catch (Exception e) {
+        }
+
+        return null;
+    }
+
+    private void dumpL2GwCache() {
+        ConcurrentMap<String, L2GatewayDevice> devices = (ConcurrentMap<String, L2GatewayDevice>) CacheUtil
+                .getCache(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME);
+        if (devices == null) {
+            System.out.println("no devices are present in cache");
+            return;
+        }
+        for (String deviceName : devices.keySet()) {
+            System.out.println("device "+ devices.get(deviceName));
+        }
+    }
+
+    private void dumpElanL2GwCache() {
+        if (elanName == null) {
+            ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>> cache =
+                    (ConcurrentMap<String, ConcurrentMap<String, L2GatewayDevice>>) CacheUtil.getCache(
+                            cacheName);
+            if (cache == null) {
+                System.out.println("no devices are present in elan cache");
+            }
+            for (String elan : cache.keySet()) {
+                print(elan, cache.get(elan));
+                System.out.println(DEMARCATION);
+                System.out.println(DEMARCATION);
+            }
+            return;
+        }
+        ConcurrentMap<String, L2GatewayDevice> elanDevices = ElanL2GwCacheUtils
+                .getAllElanL2GatewayDevicesFromCache(elanName);
+        print(elanName, elanDevices);
+    }
+
+    private void print(String elan,
+            ConcurrentMap<String, L2GatewayDevice> devices) {
+        System.out.println("Elan name : "+elan);
+        System.out.println("No of devices in elan "+devices.keySet().size());
+        for (String deviceName : devices.keySet()) {
+            System.out.println("device "+ devices.get(deviceName));
+        }
+    }
+}
index 42af14364f0b71f2c498e04dd52b28adcd53fea1..9be6b79be91038588933768a99eb44618e5cd553 100644 (file)
@@ -107,6 +107,7 @@ public class ElanForwardingEntriesHandler extends AbstractDataChangeListener<Ela
     public void deleteElanInterfaceForwardingEntries(ElanInstance elanInfo, InterfaceInfo interfaceInfo, MacEntry macEntry) {
         InstanceIdentifier<MacEntry> macEntryId = ElanUtils.getMacEntryOperationalDataPath(elanInfo.getElanInstanceName(), macEntry.getMacAddress());
         ElanUtils.delete(broker, LogicalDatastoreType.OPERATIONAL, macEntryId);
+        deleteElanInterfaceForwardingTablesList(interfaceInfo.getInterfaceName(), macEntry);
         ElanUtils.deleteMacFlows(elanInfo, interfaceInfo, macEntry);
     }
 
index 6eed6843eecb3a266924e979853cc64df28d0fbe..89f707112ad31c1bbe2f6a3a33ed474913e20a62 100644 (file)
@@ -64,6 +64,9 @@ public class ElanInstanceManager extends AbstractDataChangeListener<ElanInstance
     }
 
 
+    /**
+     * Starts listening for changes in elan.yang:elan-instance container
+     */
     public void registerListener() {
         try {
             elanInstanceListenerRegistration = broker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
@@ -86,19 +89,19 @@ public class ElanInstanceManager extends AbstractDataChangeListener<ElanInstance
     }
 
     @Override
-    protected void remove(InstanceIdentifier<ElanInstance> identifier, ElanInstance del) {
-        logger.trace("Remove ElanInstance - Key: {}, value: {}", identifier, del);
-        String elanName = del.getElanInstanceName();
+    protected void remove(InstanceIdentifier<ElanInstance> identifier, ElanInstance deletedElan) {
+        logger.trace("Remove ElanInstance - Key: {}, value: {}", identifier, deletedElan);
+        String elanName = deletedElan.getElanInstanceName();
         //check the elan Instance present in the Operational DataStore
         Elan existingElan = ElanUtils.getElanByName(elanName);
-        long elanTag = del.getElanTag();
+        long elanTag = deletedElan.getElanTag();
         //Cleaning up the existing Elan Instance
         if(existingElan != null) {
             List<String> elanInterfaces =  existingElan.getElanInterfaces();
             if(elanInterfaces != null && !elanInterfaces.isEmpty()) {
                 for (String elanInterfaceName : elanInterfaces) {
                     InstanceIdentifier<ElanInterface> elanInterfaceId = ElanUtils.getElanInterfaceConfigurationDataPathId(elanInterfaceName);
-                    elanInterfaceManager.removeElanInterface(del, elanInterfaceName);
+                    elanInterfaceManager.removeElanInterface(deletedElan, elanInterfaceName);
                     ElanUtils.delete(broker, LogicalDatastoreType.CONFIGURATION, elanInterfaceId);
                 }
             }
@@ -118,10 +121,7 @@ public class ElanInstanceManager extends AbstractDataChangeListener<ElanInstance
             return;
         } else if (update.getElanTag() == null) {
             // update the elan-Instance with new properties
-            if(original.getMacTimeout().equals(update.getMacTimeout()) && original.getDescription().equalsIgnoreCase(update.getDescription())) {
-               return;
-            }
-            ElanUtils.UpdateOperationalDataStore(broker, idManager, update);
+            ElanUtils.updateOperationalDataStore(broker, idManager, update);
             return;
         }
         elanInterfaceManager.handleunprocessedElanInterfaces(update);
@@ -131,7 +131,7 @@ public class ElanInstanceManager extends AbstractDataChangeListener<ElanInstance
     protected void add(InstanceIdentifier<ElanInstance> identifier, ElanInstance elanInstanceAdded) {
         Elan elanInfo = ElanUtils.getElanByName(elanInstanceAdded.getElanInstanceName());
         if(elanInfo == null) {
-            ElanUtils.UpdateOperationalDataStore(broker, idManager, elanInstanceAdded);
+            ElanUtils.updateOperationalDataStore(broker, idManager, elanInstanceAdded);
         }
     }
 
index 0dce5c963605dea75b6b2277afc30bc961b48657..86a5b7e92ec548c846df8aca137e73fbbbf292b2 100644 (file)
@@ -7,30 +7,64 @@
  */
 package org.opendaylight.vpnservice.elan.internal;
 
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+
 import com.google.common.base.Optional;
 import com.google.common.collect.Maps;
+
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayMulticastUtils;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayUtils;
 import org.opendaylight.vpnservice.elan.utils.ElanConstants;
 import org.opendaylight.vpnservice.elan.utils.ElanUtils;
 import org.opendaylight.vpnservice.interfacemgr.globals.InterfaceInfo;
 import org.opendaylight.vpnservice.interfacemgr.globals.InterfaceInfo.InterfaceType;
 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
-
+import org.opendaylight.vpnservice.itm.api.IITMProvider;
+import org.opendaylight.vpnservice.itm.globals.ITMConstants;
+import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener;
+import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
+import org.opendaylight.vpnservice.mdsalutil.ActionType;
+import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
 import org.opendaylight.vpnservice.mdsalutil.NwConstants;
 import org.opendaylight.vpnservice.itm.globals.ITMConstants;
 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.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.params.xml.ns.yang.servicebinding.rev151015.service.bindings.services.info.BoundServices;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
 import org.opendaylight.vpnservice.mdsalutil.*;
 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
 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.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.services.info.BoundServices;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanDpnInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanForwardingTables;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanInterfaces;
@@ -59,12 +93,17 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.math.BigInteger;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
 
 
+/**
+ * Class in charge of handling creations, modifications and removals of ElanInterfaces.
+ *
+ * @see org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.interfaces.ElanInterface
+ *
+ */
 public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterface> implements AutoCloseable {
 
     private static ElanInterfaceManager elanInterfaceManager = new ElanInterfaceManager();
@@ -157,7 +196,9 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
     public void removeElanInterface(ElanInstance elanInfo, String interfaceName) {
         String elanName = elanInfo.getElanInstanceName();
         InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
+
         if (interfaceInfo == null) {
+            // Interface does not exist in ConfigDS, so lets remove everything about that interface related to Elan
             ElanInterfaceMac elanInterfaceMac =  ElanUtils.getElanInterfaceMacByInterfaceName(interfaceName);
             if(elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) {
                List<MacEntry> macEntries = elanInterfaceMac.getMacEntry();
@@ -177,10 +218,10 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                 Elan updateElanState = new ElanBuilder().setElanInterfaces(elanInterfaces).setName(elanName).setKey(new ElanKey(elanName)).build();
                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanName), updateElanState);
             }
-            return;
+        } else {
+            removeElanInterface(elanInfo, interfaceInfo);
+            unbindService(elanInfo, interfaceName);
         }
-        removeElanInterface(elanInfo, interfaceInfo);
-        unbindService(elanInfo, interfaceName);
     }
 
     private void removeElanInterface(ElanInstance elanInfo, InterfaceInfo interfaceInfo) {
@@ -193,11 +234,22 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
         InstanceIdentifier<ElanInterfaceMac> elanInterfaceId = ElanUtils.getElanInterfaceMacEntriesOperationalDataPath(interfaceName);
         Optional<ElanInterfaceMac> existingElanInterface = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanInterfaceId);
         if(existingElanInterface.isPresent()) {
-            List<MacEntry> macEntries = existingElanInterface.get().getMacEntry();
-            if(macEntries != null && !macEntries.isEmpty()) {
+            List<PhysAddress> macAddresses = new ArrayList<PhysAddress>();
+            List<MacEntry> existingMacEntries = existingElanInterface.get().getMacEntry();
+            List<MacEntry> macEntries = new ArrayList<>();
+            if (existingMacEntries != null && !existingMacEntries.isEmpty()) {
+                macEntries.addAll(existingMacEntries);
+            }
+            if(!macEntries.isEmpty()) {
                 for (MacEntry macEntry : macEntries) {
                     logger.debug("removing the  mac-entry:{} present on elanInterface:{}", macEntry.getMacAddress().getValue(), interfaceName);
                     elanForwardingEntriesHandler.deleteElanInterfaceForwardingEntries(elanInfo, interfaceInfo, macEntry);
+                    macAddresses.add(macEntry.getMacAddress());
+                }
+
+                // Removing all those MACs from External Devices belonging to this ELAN
+                if ( elanInfo.getVni() != null && elanInfo.getVni() != 0 ) {
+                    ElanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo, macAddresses);
                 }
             }
         }
@@ -243,7 +295,13 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
         if(dpnInterfaces != null) {
             List<String> interfaceLists = dpnInterfaces.getInterfaces();
             interfaceLists.remove(interfaceName);
-            updateElanDpnInterfacesList(elanName, dpId, interfaceLists);
+
+            if (interfaceLists == null || interfaceLists.isEmpty()) {
+                deleteElanDpnInterface(elanName, dpId);
+                ElanL2GatewayMulticastUtils.updateRemoteMcastMacOnElanL2GwDevices(elanName);
+            } else {
+                updateElanDpnInterfacesList(elanName, dpId, interfaceLists);
+            }
         }
     }
 
@@ -290,12 +348,16 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
         String elanInstanceName = elanInterfaceAdded.getElanInstanceName();
         String interfaceName = elanInterfaceAdded.getName();
         InterfaceInfo interfaceInfo = interfaceManager.getInterfaceInfo(interfaceName);
+        if (interfaceInfo == null) {
+            logger.warn("Interface {} is removed from Interface Oper DS due to port down ", interfaceName);
+            return;
+        }
         ElanInstance elanInstance = ElanUtils.getElanInstanceByName(elanInstanceName);
 
         if (elanInstance == null) {
             elanInstance = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName).setDescription(elanInterfaceAdded.getDescription()).build();
             //Add the ElanInstance in the Configuration data-store
-            ElanUtils.UpdateOperationalDataStore(broker, idManager, elanInstance);
+            ElanUtils.updateOperationalDataStore(broker, idManager, elanInstance);
             elanInstance = ElanUtils.getElanInstanceByName(elanInstanceName);
         }
 
@@ -327,26 +389,75 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
         }
     }
 
+    void programRemoteDmacFlow(ElanInstance elanInstance, InterfaceInfo interfaceInfo){
+        ElanDpnInterfacesList elanDpnInterfacesList =  ElanUtils.getElanDpnInterfacesList(elanInstance.getElanInstanceName());
+        List<DpnInterfaces> dpnInterfaceLists =  elanDpnInterfacesList.getDpnInterfaces();
+        for(DpnInterfaces dpnInterfaces : dpnInterfaceLists){
+            if(dpnInterfaces.getDpId().equals(interfaceInfo.getDpId())) {
+                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) {
+                        PhysAddress physAddress = macEntry.getMacAddress();
+                        ElanUtils.setupRemoteDmacFlow(interfaceInfo.getDpId(), remoteInterface.getDpId(),
+                                remoteInterface.getInterfaceTag(),
+                                elanInstance.getElanTag(),
+                                physAddress.getValue(),
+                                elanInstance.getElanInstanceName());
+                    }
+                }
+            }
+        }
+    }
+
     void addElanInterface(ElanInterface elanInterface, InterfaceInfo interfaceInfo, ElanInstance elanInstance) {
+        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();
         List<PhysAddress> staticMacAddresses = elanInterface.getStaticMacEntries();
+
         Elan elanInfo = ElanUtils.getElanByName(elanInstanceName);
-        BigInteger dpId = null;
         if(elanInfo == null) {
-            ElanUtils.UpdateOperationalDataStore(broker, idManager, elanInstance);
-        }
-        if(interfaceInfo != null) {
-            dpId = interfaceInfo.getDpId();
+            ElanUtils.updateOperationalDataStore(broker, idManager, elanInstance);
         }
+
+        // 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;
         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()) {
+                // ELAN's 1st ElanInterface added to this DPN
                 createElanInterfacesList(elanInstanceName, interfaceName, dpId);
+                /*
+                 * 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.
+                 */
+                programRemoteDmacFlow(elanInstance, interfaceInfo);
+                // The 1st ElanInterface in a DPN must program the Ext Tunnel table, but only if Elan has VNI
+                if ( elanInstance.getVni() != null && elanInstance.getVni().longValue() != 0 ) {
+                    setExternalTunnelTable(dpId, elanInstance);
+                }
+                ElanL2GatewayMulticastUtils.updateRemoteMcastMacOnElanL2GwDevices(elanInstanceName);
             } else {
                 List<String> elanInterfaces = existingElanDpnInterfaces.get().getInterfaces();
                 elanInterfaces.add(interfaceName);
+                if (elanInterfaces.size() == 1) {//1st dpn interface
+                    ElanL2GatewayMulticastUtils.updateRemoteMcastMacOnElanL2GwDevices(elanInstanceName);
+                }
                 updateElanDpnInterfacesList(elanInstanceName, dpId, elanInterfaces);
             }
         }
@@ -356,11 +467,12 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
         //Add interface to the ElanInterfaceForwardingEntires Container
         createElanInterfaceTablesList(interfaceName);
         createElanStateList(elanInstanceName, interfaceName);
-        if(interfaceInfo != null) {
+        if (interfaceInfo != null) {
             installFlowsAndGroups(elanInstance, interfaceInfo);
         }
         // add the static mac addresses
-        if(staticMacAddresses != null) {
+        if (staticMacAddresses != null) {
+            boolean isInterfaceOperational = isOperational(interfaceInfo);
             for (PhysAddress physAddress : staticMacAddresses) {
                 InstanceIdentifier<MacEntry> macId = getMacEntryOperationalDataPath(elanInstanceName, physAddress);
                 Optional<MacEntry> existingMacEntry = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, macId);
@@ -369,28 +481,18 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                 } else {
                     elanForwardingEntriesHandler.addElanInterfaceForwardingTableList(elanInstance, interfaceName, physAddress);
                 }
-                if(interfaceInfo != null && isOperational(interfaceInfo)) {
-                    logger.debug("Installing Static Mac-Entry on the Elan Interface:{} with MacAddress:{}", interfaceInfo, physAddress.getValue());
+
+                if ( isInterfaceOperational ) {
+                    // Setting SMAC, DMAC, UDMAC in this DPN and also in other DPNs
                     ElanUtils.setupMacFlows(elanInstance, interfaceInfo, ElanConstants.STATIC_MAC_TIMEOUT, physAddress.getValue());
                 }
             }
-        }
-    }
 
-    private Map<BigInteger, List<String>> readFePortsDbForElan(String elanName) {
-        ElanDpnInterfacesList elanDpnInterfacesList = ElanUtils.getElanDpnInterfacesList(elanName);
-        HashMap<BigInteger, List<String>> fePortsDb = Maps.newHashMap();
-        if (elanDpnInterfacesList == null) {
-            return fePortsDb;
-        }
-        List<DpnInterfaces> dpnInterfaces = elanDpnInterfacesList.getDpnInterfaces();
-        if (dpnInterfaces == null) {
-            return fePortsDb;
-        }
-        for (DpnInterfaces dpnInterface : dpnInterfaces) {
-            fePortsDb.put(dpnInterface.getDpId(), dpnInterface.getInterfaces());
+            if( isInterfaceOperational ) {
+                // Add MAC in TOR's remote MACs via OVSDB. Outside of the loop on purpose.
+                ElanL2GatewayUtils.installMacsInElanExternalDevices(elanInstance, dpId, staticMacAddresses);
+            }
         }
-        return fePortsDb;
     }
 
     protected void removeInterfaceStaticMacEntires(String elanInstanceName, String interfaceName, PhysAddress physAddress) {
@@ -441,7 +543,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
 
         Flow flowEntry = MDSALUtil.buildFlowNew(ElanConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(ElanConstants.ELAN_FILTER_EQUALS_TABLE, 1000+ifTag),
                 10, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getMatchesForFilterEqualsLPortTag(ifTag),
-                getInstructionsDrop());
+                MDSALUtil.buildInstructionsDrop());
 
         mdsalManager.installFlow(interfaceInfo.getDpId(), flowEntry);
     }
@@ -455,7 +557,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
 
         Flow flowEntity = MDSALUtil.buildFlowNew(ElanConstants.ELAN_FILTER_EQUALS_TABLE, getFlowRef(ElanConstants.ELAN_FILTER_EQUALS_TABLE, 1000+ifTag),
                 10, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_FILTER_EQUALS.add(BigInteger.valueOf(ifTag)), getMatchesForFilterEqualsLPortTag(ifTag),
-                getInstructionsDrop());
+                MDSALUtil.buildInstructionsDrop());
 
         mdsalManager.removeFlow(interfaceInfo.getDpId(), flowEntity);
     }
@@ -472,7 +574,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
             for(DpnInterfaces dpnInterface : dpnInterfaceses) {
                if(ElanUtils.isDpnPresent(dpnInterface.getDpId()) && dpnInterface.getDpId() != dpnId && dpnInterface.getInterfaces() != null && !dpnInterface.getInterfaces().isEmpty()) {
                    try {
-                       List<Action> listAction = ElanUtils.getItmEgressAction(dpnId, dpnInterface.getDpId(), (int) elanTag);
+                       List<Action> listAction = ElanUtils.getInternalItmEgressAction(dpnId, dpnInterface.getDpId(), elanTag);
                        listBuckets.add(MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
                        bucketId++;
                    } catch (Exception ex) {
@@ -480,6 +582,8 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                    }
                }
             }
+            List<Bucket> elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId, bucketId);
+            listBuckets.addAll(elanL2GwDevicesBuckets);
         }
         return listBuckets;
     }
@@ -495,7 +599,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
             for(DpnInterfaces dpnInterface : dpnInterfaceses) {
                 if(ElanUtils.isDpnPresent(dpnInterface.getDpId()) && dpnInterface.getDpId() != dpnId && dpnInterface.getInterfaces() != null && !dpnInterface.getInterfaces().isEmpty()) {
                     try {
-                        List<Action> listActionInfo = ElanUtils.getItmEgressAction(dpnId, dpnInterface.getDpId(), (int) elanTag);
+                        List<Action> listActionInfo = ElanUtils.getInternalItmEgressAction(dpnId, dpnInterface.getDpId(), elanTag);
                         listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, 0, bucketId, 0xffffffffL, 0xffffffffL));
                         bucketId++;
                     } catch (Exception ex) {
@@ -503,6 +607,9 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                     }
                 }
             }
+
+            List<Bucket> elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId, bucketId);
+            listBucketInfo.addAll(elanL2GwDevicesBuckets);
         }
         return listBucketInfo;
     }
@@ -536,7 +643,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                         if (ElanUtils.isDpnPresent(otherFes.getDpId()) && otherFes.getDpId() != dpnInterface.getDpId()
                                 && otherFes.getInterfaces() != null && ! otherFes.getInterfaces().isEmpty()) {
                             try {
-                                List<Action> remoteListActionInfo = ElanUtils.getItmEgressAction(dpnInterface.getDpId(), otherFes.getDpId(), (int) elanTag);
+                                List<Action> remoteListActionInfo = ElanUtils.getInternalItmEgressAction(dpnInterface.getDpId(), otherFes.getDpId(), elanTag);
                                 remoteListBucketInfo.add(MDSALUtil.buildBucket(remoteListActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT,MDSALUtil.WATCH_GROUP));
                                 bucketId++;
                             } catch (Exception ex) {
@@ -545,7 +652,11 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                             }
                         }
                     }
-                    if(remoteListBucketInfo.size() == 0) {
+                    List<Bucket> elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId,
+                            bucketId);
+                    remoteListBucketInfo.addAll(elanL2GwDevicesBuckets);
+
+                    if (remoteListBucketInfo.size() == 0) {
                         logger.debug( "No ITM is present on Dpn - {} " ,dpnInterface.getDpId());
                         continue;
                     }
@@ -560,32 +671,27 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                                                InterfaceInfo interfaceInfo, BigInteger dstDpId) {
         int elanTag = elanInfo.getElanTag().intValue();
         long groupId = ElanUtils.getElanRemoteBCGID(elanTag);
-        DpnInterfaces dpnInterfaces = ElanUtils.getElanInterfaceInfoByElanDpn(elanInfo.getElanInstanceName(), interfaceInfo.getDpId());
         List<DpnInterfaces> elanDpns = ElanUtils.getInvolvedDpnsInElan(elanInfo.getElanInstanceName());
         if(elanDpns != null) {
             for(DpnInterfaces dpnInterface : elanDpns) {
                 int bucketId = 0;
                 List<Bucket> remoteListBucket = new ArrayList<Bucket>();
                 if(ElanUtils.isDpnPresent(dstDpId) && dpnInterface.getDpId().equals(dstDpId) && dpnInterface.getInterfaces() != null && !dpnInterface.getInterfaces().isEmpty()) {
-                    for(String ifName : dpnInterfaces.getInterfaces()) {
-                        // 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;
-                        }
-
-                        remoteListBucket.add(MDSALUtil.buildBucket(getInterfacePortActions(ifInfo), MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
-                        bucketId++;
-                    }
                     try {
-                        List<Action> remoteListActionInfo = ElanUtils.getItmEgressAction(interfaceInfo.getDpId(), dstDpId, (int) elanTag);
+                        List<Action> remoteListActionInfo = ElanUtils.getInternalItmEgressAction(interfaceInfo.getDpId(), dstDpId, elanTag);
                         remoteListBucket.add(MDSALUtil.buildBucket(remoteListActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
                         bucketId++;
                     } catch (Exception ex) {
                         logger.error( "Logical Group Interface not found between source Dpn - {}, destination Dpn - {} " ,dpnInterface.getDpId(), dstDpId);
                         return;
                     }
+                    List<Action> remoteListActionInfo = new ArrayList<Action>();
+                    remoteListActionInfo.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(ElanUtils.getElanLocalBCGID(elanTag))}).buildAction());
+                    remoteListBucket.add(MDSALUtil.buildBucket(remoteListActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
+
+                    List<Bucket> elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dstDpId, bucketId);
+                    remoteListBucket.addAll(elanL2GwDevicesBuckets);
+
                     Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(remoteListBucket));
                     mdsalManager.syncInstallGroup(interfaceInfo.getDpId(), group, ElanConstants.DELAY_TIME_IN_MILLISECOND);
                     break;
@@ -611,12 +717,55 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
         return mkMatches;
     }
 
+
+    private List<MatchInfo> buildMatchesForVni(Long vni) {
+        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+        MatchInfo match = new MatchInfo(MatchFieldType.tunnel_id,
+                                        new BigInteger[]{BigInteger.valueOf(vni)} );
+        mkMatches.add(match);
+        return mkMatches;
+    }
+
     private List<Instruction> getInstructionsForOutGroup(
             long groupId) {
         List<Instruction> mkInstructions = new ArrayList<Instruction>();
         List <Action> actions = new ArrayList <Action> ();
         actions.add(new ActionInfo(ActionType.group, new String[]{Long.toString(groupId)}).buildAction());
-        mkInstructions.add(ElanUtils.getWriteActionInstruction(actions));
+        mkInstructions.add(MDSALUtil.getWriteActionsInstruction(actions, 0));
+        return mkInstructions;
+    }
+
+    private List<MatchInfo> getMatchesForElanTag(long elanTag, boolean isSHFlagSet) {
+        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+        // Matching metadata
+        mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                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<InstructionInfo>();
+        mkInstructions.add(new InstructionInfo(InstructionType.write_metadata,
+                                               new BigInteger[] {
+                                                       ElanUtils.getElanMetadataLabel(elanTag),
+                                                       ElanUtils.getElanMetadataMask()
+                                               } ) );
+        // TODO (eperefr) We should point to SMAC or DMAC depending on a configuration property to enable
+        // mac learning
+        mkInstructions.add(new InstructionInfo(InstructionType.goto_table,
+                                               new long[] { ElanConstants.ELAN_DMAC_TABLE }));
+
         return mkInstructions;
     }
 
@@ -626,6 +775,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
     }
 
     public void installMacAddressTables(ElanInstance elanInfo, InterfaceInfo interfaceInfo) {
+
         String interfaceName = interfaceInfo.getInterfaceName();
         BigInteger currentDpn = interfaceInfo.getDpId();
         ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(interfaceName);
@@ -633,7 +783,11 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
             List<MacEntry> macEntries =  elanInterfaceMac.getMacEntry();
             for(MacEntry macEntry : macEntries) {
                 PhysAddress physAddress = macEntry.getMacAddress();
-                ElanUtils.setupMacFlows(elanInfo, interfaceInfo, macEntry.isIsStaticAddress() ? ElanConstants.STATIC_MAC_TIMEOUT : elanInfo.getMacTimeout(), physAddress.getValue());
+                ElanUtils.setupMacFlows(elanInfo,
+                                        interfaceInfo,
+                                        macEntry.isIsStaticAddress()
+                                          ? ElanConstants.STATIC_MAC_TIMEOUT
+                                          : elanInfo.getMacTimeout(), physAddress.getValue());
             }
             //Programming the remoteDMACFlows
             ElanDpnInterfacesList elanDpnInterfacesList =  ElanUtils.getElanDpnInterfacesList(elanInfo.getElanInstanceName());
@@ -653,7 +807,11 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                     if(remoteMacEntries != null) {
                         for (MacEntry macEntry : remoteMacEntries) {
                             PhysAddress physAddress = macEntry.getMacAddress();
-                            ElanUtils.setupRemoteDmacFlow(currentDpn, remoteInterface.getDpId(), remoteInterface.getInterfaceTag(), elanInfo.getElanTag(), physAddress.getValue(), elanInfo.getElanInstanceName());
+                            ElanUtils.installDmacFlowsToInternalRemoteMac(currentDpn, remoteInterface.getDpId(),
+                                    remoteInterface.getInterfaceTag(),
+                                    elanInfo.getElanTag(),
+                                    physAddress.getValue(),
+                                    elanInfo.getElanInstanceName());
                         }
                     }
                 }
@@ -695,7 +853,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
             // 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 (ifInfo == null || !isOperational(ifInfo)) {
+            if (!isOperational(ifInfo)) {
                 continue;
             }
 
@@ -707,13 +865,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
 
         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBucket));
         logger.trace("installing the localBroadCast Group:{}", group);
-        // In the case of OVS disconnected we receive null object when we query Interface Operation datastore
-        // so the size of the bucket will be zero
-        if(listBucket.size() == 0) {
-            mdsalManager.syncRemoveGroup(dpnId, group);
-        } else {
-            mdsalManager.syncInstallGroup(dpnId, group, ElanConstants.DELAY_TIME_IN_MILLISECOND);
-        }
+        mdsalManager.syncInstallGroup(dpnId, group, ElanConstants.DELAY_TIME_IN_MILLISECOND);
     }
 
     public void setupLocalBroadcastGroups(ElanInstance elanInfo, InterfaceInfo interfaceInfo) {
@@ -727,7 +879,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
             // 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 (ifInfo == null || !isOperational(ifInfo)) {
+            if (!isOperational(ifInfo)) {
                 continue;
             }
 
@@ -735,16 +887,9 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
             bucketId++;
         }
 
-
         Group group = MDSALUtil.buildGroup(groupId, elanInfo.getElanInstanceName(), GroupTypes.GroupAll, MDSALUtil.buildBucketLists(listBucket));
         logger.trace("installing the localBroadCast Group:{}", group);
-        // In the case of OVS disconnected we receive null object for the Interface Operation datastore
-        // so the size of the bucket will be zero
-        if(listBucket.size() == 0) {
-            mdsalManager.syncRemoveGroup(dpnId, group);
-        } else {
-            mdsalManager.syncInstallGroup(dpnId, group, ElanConstants.DELAY_TIME_IN_MILLISECOND);
-        }
+        mdsalManager.syncInstallGroup(dpnId, group, ElanConstants.DELAY_TIME_IN_MILLISECOND);
     }
 
     public void removeLocalBroadcastGroup(ElanInstance elanInfo, InterfaceInfo interfaceInfo) {
@@ -772,6 +917,49 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
         mdsalManager.syncRemoveGroup(dpnId, group);
     }
 
+    /**
+     * 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(elanInfo.getVni()),
+                                                          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 (eperefr): 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.setFlowId(flowId);
+        mdsalManager.removeFlow(flowEntity);
+    }
+
     public void setupTerminateServiceTable(ElanInstance elanInfo, InterfaceInfo interfaceInfo) {
         long elanTag = elanInfo.getElanTag();
         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE, getFlowRef(NwConstants.INTERNAL_TUNNEL_TABLE, elanTag),
@@ -783,32 +971,40 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
 
     public void setupUnknownDMacTable(ElanInstance elanInfo, InterfaceInfo interfaceInfo) {
         long elanTag = elanInfo.getElanTag();
-        Flow flowEntity = MDSALUtil.buildFlowNew(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, getFlowRef(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag),
-                5, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_UNKNOWN_DMAC.add(BigInteger.valueOf(elanTag)), getMatchesForElanTag(elanTag),
+        Flow flowEntity = MDSALUtil.buildFlowNew(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, getUnknownDmacFlowRef(ElanConstants.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.installFlow(interfaceInfo.getDpId(), flowEntity);
+
+        // only if vni is present in elanInfo, perform the following
+        if (elanInfo.getVni() != null && elanInfo.getVni() != 0) {
+           Flow flowEntity2 = MDSALUtil.buildFlowNew(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, getUnknownDmacFlowRef(ElanConstants.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.installFlow(interfaceInfo.getDpId(), flowEntity2);
+        }
+
     }
 
+
     private void removeStaticELanFlows(final ElanInstance elanInfo, final InterfaceInfo interfaceInfo) {
         BigInteger dpId = interfaceInfo.getDpId();
-        long elanTag = elanInfo.getElanTag();
         /*
          * If there are not elan ports, remove the unknown smac and default dmac
          * flows
          */
         DpnInterfaces dpnInterfaces = ElanUtils.getElanInterfaceInfoByElanDpn(elanInfo.getElanInstanceName(), dpId);
-        if(dpnInterfaces == null) {
-            return;
-        }
-        List <String> elanInterfaces = dpnInterfaces.getInterfaces();
-        if (elanInterfaces == null || elanInterfaces.isEmpty()) {
-
+        if (dpnInterfaces == null || dpnInterfaces.getInterfaces() == null || dpnInterfaces.getInterfaces().isEmpty()) {
+            // No more Elan Interfaces in this DPN
             logger.debug("deleting the elan: {} present on dpId: {}", elanInfo.getElanInstanceName(), dpId);
             removeDefaultTermFlow(dpId, elanInfo.getElanTag());
             removeUnknownDmacFlow(dpId, elanInfo);
             removeElanBroadcastGroup(elanInfo, interfaceInfo);
             removeLocalBroadcastGroup(elanInfo, interfaceInfo);
+            if ( elanInfo.getVni() != null && elanInfo.getVni().longValue() != 0 ) {
+                unsetExternalTunnelTable(dpId, elanInfo);
+            }
             removeFilterEqualsTable(elanInfo, interfaceInfo);
         } else {
             setupElanBroadcastGroups(elanInfo, interfaceInfo);
@@ -818,8 +1014,23 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
     }
 
     private void removeUnknownDmacFlow(BigInteger dpId, ElanInstance elanInfo) {
-        Flow flow = getUnknownDmacFlowEntity(dpId, elanInfo);
+//        Flow flow = getUnknownDmacFlowEntity(dpId, elanInfo);
+//        mdsalManager.removeFlow(dpId, flow);
+        Flow flow = new FlowBuilder().setId(new FlowId(getUnknownDmacFlowRef(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE,
+                                                                             elanInfo.getElanTag(), /*SH flag*/ false)))
+                                     .setTableId(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE)
+                                     .build();
         mdsalManager.removeFlow(dpId, flow);
+
+        if ( elanInfo.getVni() != null && elanInfo.getVni() > 0 ) {
+           Flow flow2 = new FlowBuilder().setId(new FlowId(getUnknownDmacFlowRef(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE,
+                                                                                elanInfo.getElanTag(), /*SH flag*/ true)))
+                                        .setTableId(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE)
+                                        .build();
+           mdsalManager.removeFlow(dpId, flow2);
+        }
+
+
     }
 
     private void removeDefaultTermFlow(BigInteger dpId, long elanTag) {
@@ -855,35 +1066,21 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                 ElanUtils.DEFAULT_CALLBACK);
     }
 
-    private Flow getUnknownDmacFlowEntity(BigInteger dpId, ElanInstance elanInfo) {
-        long elanTag = elanInfo.getElanTag();
-        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
-        // Matching metadata
-        mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                ElanUtils.getElanMetadataLabel(elanTag),
-                MetaDataUtil.METADATA_MASK_SERVICE }));
-
-        List<Instruction> mkInstructions = new ArrayList<Instruction>();
-        List <Action> actionsInfos = new ArrayList <Action> ();
-        actionsInfos.add(new ActionInfo(ActionType.group, new String[]{Long.toString(ElanUtils.getElanRemoteBCGID(elanTag))}, 0).buildAction());
-        mkInstructions.add(ElanUtils.getWriteActionInstruction(actionsInfos));
-
-        Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, getFlowRef(ElanConstants.ELAN_UNKNOWN_DMAC_TABLE, elanTag),
-                5, elanInfo.getElanInstanceName(), 0, 0, ElanConstants.COOKIE_ELAN_UNKNOWN_DMAC.add(BigInteger.valueOf(elanTag)),
-                mkMatches, mkInstructions);
-        return flow;
-    }
-
     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<Action>();
         int actionKey = 0;
         listAction.add((new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {BigInteger.valueOf(interfaceInfo.getInterfaceTag())}, actionKey)).buildAction());
         actionKey++;
-        listAction.add((new ActionInfo(ActionType.nx_resubmit, new String[] {Short.toString((short)55)}, actionKey)).buildAction());
+        listAction.add((new ActionInfo(ActionType.nx_resubmit,
+                new String[] {String.valueOf(ElanConstants.ELAN_FILTER_EQUALS_TABLE)}, actionKey)).buildAction());
         return listAction;
     }
 
@@ -894,6 +1091,19 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
                 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) {
+        MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL,
+                ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId));
+    }
+
     private List<String> createElanInterfacesList(String elanInstanceName, String interfaceName, BigInteger dpId) {
         List<String> interfaceNames = new ArrayList<String>();
         interfaceNames.add(interfaceName);
@@ -931,6 +1141,9 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
     }
 
     private boolean isOperational(InterfaceInfo interfaceInfo) {
+        if (interfaceInfo == null) {
+            return false;
+        }
         return ((interfaceInfo.getOpState() == InterfaceInfo.InterfaceOpState.UP) && (interfaceInfo.getAdminState() == InterfaceInfo.InterfaceAdminState.ENABLED));
     }
 
@@ -1018,7 +1231,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
         }
     }
 
-    public void handleInterfaceUpated(InterfaceInfo interfaceInfo, ElanInstance elanInstance, boolean isStateUp) {
+    public void handleInterfaceUpdated(InterfaceInfo interfaceInfo, ElanInstance elanInstance, boolean isStateUp) {
         BigInteger dpId = interfaceInfo.getDpId();
         String elanName = elanInstance.getElanInstanceName();
         String ifName = interfaceInfo.getInterfaceName();
@@ -1052,6 +1265,15 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
             logger.trace("ElanInterface Service is removed for the interface:{}", ifName);
             elanInterfaceManager.removeMacAddressTables(elanInstance, interfaceInfo);
             elanInterfaceManager.removeFlowsAndGroups(elanInstance, interfaceInfo);
+
+            // Removing MACs from External Devices belonging to this ELAN
+            if (elanInstance.getVni() != null && elanInstance.getVni() != 0) {
+                List<PhysAddress> macAddresses = ElanUtils
+                        .getElanInterfaceMacAddresses(interfaceInfo.getInterfaceName());
+                if (macAddresses != null && !macAddresses.isEmpty()) {
+                    ElanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInstance, macAddresses);
+                }
+            }
         }
     }
 
@@ -1072,16 +1294,97 @@ public class ElanInterfaceManager extends AbstractDataChangeListener<ElanInterfa
         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {
                 BigInteger.valueOf(LportTag)}));
         return mkMatches;
+    }
 
+    public void updateElanBroadcastGroup(ElanInstance elanInfo) {
+        int bucketId = 0;
+        long groupId = ElanUtils.getElanRemoteBCGID(elanInfo.getElanTag());
 
+        List<DpnInterfaces> dpns = ElanUtils.getInvolvedDpnsInElan(elanInfo
+                .getElanInstanceName());
+        if (dpns == null) {
+            return;
+        }
+        for (DpnInterfaces dpn : dpns) {
+            bucketId = 0;
+            List<Bucket> listBucket = new ArrayList<Bucket>();
+            bucketId = getLocalBcGroupBuckets(dpn, listBucket, bucketId);
+            getRemoteBCGroupBuckets(elanInfo, dpn.getDpId(), listBucket,
+                    bucketId);
+            Group group = MDSALUtil.buildGroup(groupId,
+                    elanInfo.getElanInstanceName(), GroupTypes.GroupAll,
+                    MDSALUtil.buildBucketLists(listBucket));
+            logger.trace("installing the localBroadCast Group:{}", group);
+            mdsalManager.syncInstallGroup(dpn.getDpId(), group,
+                    ElanConstants.DELAY_TIME_IN_MILLISECOND);
+        }
     }
 
-    private List<Instruction> getInstructionsDrop() {
-        List<Instruction> mkInstructions = new ArrayList<Instruction>();
-        List <Action> actionsInfos = new ArrayList <Action> ();
-        actionsInfos.add(new ActionInfo(ActionType.drop_action, new String[]{}).buildAction());
-        mkInstructions.add(ElanUtils.getWriteActionInstruction(actionsInfos));
-        return mkInstructions;
+    private int getLocalBcGroupBuckets(DpnInterfaces dpn,
+            List<Bucket> listBucket, int bucketId) {
+        for (String intf : dpn.getInterfaces()) {
+            InterfaceInfo ifInfo = interfaceManager.getInterfaceInfo(intf);
+            if (!isOperational(ifInfo)) {
+                continue;
+            }
+            listBucket.add(MDSALUtil.buildBucket(
+                    getInterfacePortActions(ifInfo), MDSALUtil.GROUP_WEIGHT,
+                    bucketId, MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
+            bucketId++;
+        }
+        return bucketId;
+    }
+
+    private void getRemoteBCGroupBuckets(ElanInstance elanInfo,
+            BigInteger dpnId, List<Bucket> listBucket, int bucketId) {
+        int elanTag = elanInfo.getElanTag().intValue();
+        ElanDpnInterfacesList elanDpns = ElanUtils
+                .getElanDpnInterfacesList(elanInfo.getElanInstanceName());
+        if (elanDpns != null) {
+            List<DpnInterfaces> dpnInterfaceses = elanDpns.getDpnInterfaces();
+            for (DpnInterfaces dpnInterface : dpnInterfaceses) {
+                if (ElanUtils.isDpnPresent(dpnInterface.getDpId())
+                        && dpnInterface.getDpId() != dpnId
+                        && dpnInterface.getInterfaces() != null
+                        && !dpnInterface.getInterfaces().isEmpty()) {
+                    try {
+                        List<Action> listActionInfo = ElanUtils
+                                .getInternalItmEgressAction(dpnId,
+                                        dpnInterface.getDpId(), elanTag);
+                        listBucket.add(MDSALUtil.buildBucket(listActionInfo, 0,
+                                bucketId, 0xffffffffL, 0xffffffffL));
+                        bucketId++;
+                    } catch (Exception ex) {
+                        logger.error(
+                                "Logical Group Interface not found between source Dpn - {}, destination Dpn - {} ",
+                                dpnId, dpnInterface.getDpId());
+                    }
+                }
+            }
+        }
+        List<Bucket> elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId, bucketId);
+        listBucket.addAll(elanL2GwDevicesBuckets);
+    }
+
+    public static List<Bucket> getRemoteBCGroupBucketsOfElanL2GwDevices(ElanInstance elanInfo, BigInteger dpnId,
+            int bucketId) {
+        List<Bucket> listBucketInfo = new ArrayList<Bucket>();
+        ConcurrentMap<String, L2GatewayDevice> map = ElanL2GwCacheUtils
+                .getAllElanL2GatewayDevicesFromCache(elanInfo.getElanInstanceName());
+        for (L2GatewayDevice device : map.values()) {
+            String interfaceName = ElanL2GatewayUtils.getExternalTunnelInterfaceName(String.valueOf(dpnId),
+                    device.getHwvtepNodeId());
+            if (interfaceName == null) {
+                continue;
+            }
+            List<Action> listActionInfo = ElanUtils.buildItmEgressActions(interfaceName, elanInfo.getVni());
+            listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId,
+                    MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP));
+            bucketId++;
+        }
+        return listBucketInfo;
     }
 
 }
+
+
index 256e8b267ba3181062afeb962b0b6995d9c96970..470f6237bf29c32222404762844dce3b6a00bc93 100644 (file)
@@ -82,7 +82,7 @@ public class ElanInterfaceStateChangeListener extends AbstractDataChangeListener
         }
 
         logger.trace("ElanService Interface Operational state has changes for Interface:{}", interfaceName);
-        elanInterfaceManager.handleInterfaceUpated(interfaceInfo, elanInfo , isStateUp);
+        elanInterfaceManager.handleInterfaceUpdated(interfaceInfo, elanInfo , isStateUp);
     }
 
     @Override
@@ -95,7 +95,7 @@ public class ElanInterfaceStateChangeListener extends AbstractDataChangeListener
             return;
         }
         NodeConnectorId nodeConnectorId = new NodeConnectorId(delIf.getLowerLayerIf().get(0));
-        BigInteger dpId = MDSALUtil.getDpnIdFromNodeName(nodeConnectorId.getValue());
+        BigInteger dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId));
         InterfaceInfo interfaceInfo = new InterfaceInfo(dpId, nodeConnectorId.getValue());
         interfaceInfo.setInterfaceName(interfaceName);
         interfaceInfo.setInterfaceType(InterfaceInfo.InterfaceType.VLAN_INTERFACE);
@@ -162,7 +162,7 @@ public class ElanInterfaceStateChangeListener extends AbstractDataChangeListener
         if (tunnelList.getInternalTunnel() != null) {
             List<InternalTunnel> internalTunnels = tunnelList.getInternalTunnel();
             for (InternalTunnel tunnel : internalTunnels) {
-                if (internalTunnel.getTunnelInterfaceName().equalsIgnoreCase(interfaceName)) {
+                if (tunnel.getTunnelInterfaceName().equalsIgnoreCase(interfaceName)) {
                     internalTunnel = tunnel;
                     break;
                 }
index 05a808ce294eb9d08cb6029923dea77586757301..679d2c9ab57f7ac706fad22d8175f05520bbbfeb 100644 (file)
@@ -71,6 +71,10 @@ public class ElanNodeListener extends AbstractDataChangeListener<Node> {
     protected void add(InstanceIdentifier<Node> identifier, Node add) {
         NodeId nodeId = add.getId();
         String[] node =  nodeId.getValue().split(":");
+        if(node.length < 2) {
+            logger.warn("Unexpected nodeId {}", nodeId.getValue());
+            return;
+        }
         BigInteger dpId = new BigInteger(node[1]);
         createTableMissEntry(dpId);
     }
index 04de7e3a953a858e4cb01425c574ff0dab4391b4..dbf83ddc6021537469ddef6649222106c157598b 100755 (executable)
@@ -7,24 +7,27 @@
  */
 package org.opendaylight.vpnservice.elan.internal;
 
+import java.math.BigInteger;
+
+import org.opendaylight.controller.liblldp.NetUtils;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayUtils;
 import org.opendaylight.vpnservice.elan.utils.ElanConstants;
 import org.opendaylight.vpnservice.elan.utils.ElanUtils;
-//import org.opendaylight.vpnservice.interfacemgr.globals.InterfaceInfo;
 import org.opendaylight.vpnservice.interfacemgr.globals.InterfaceInfo;
 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
 import org.opendaylight.vpnservice.mdsalutil.NWUtil;
-import org.opendaylight.controller.liblldp.NetUtils;
 import org.opendaylight.vpnservice.mdsalutil.packet.Ethernet;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
 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.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.NoMatch;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketInReason;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.interfacemgr.impl.rev150325.InterfacemgrImpl;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.instances.ElanInstance;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.state.Elan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.tag.name.map.ElanTagName;
@@ -36,8 +39,12 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Optional;
 import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
 
+@SuppressWarnings("deprecation")
 public class ElanPacketInHandler implements PacketProcessingListener {
 
     private final DataBroker broker;
@@ -72,15 +79,18 @@ public class ElanPacketInHandler implements PacketProcessingListener {
 
                 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
 
-                IfIndexInterface interfaceInfo = ElanUtils.getInterfaceInfoByInterfaceTag(portTag);
-                if (interfaceInfo == null) {
+                Optional<IfIndexInterface> interfaceInfoOp = ElanUtils.getInterfaceInfoByInterfaceTag(portTag);
+                if (!interfaceInfoOp.isPresent()) {
                     logger.warn("There is no interface for given portTag {}", portTag);
                     return;
                 }
-                String interfaceName = interfaceInfo.getInterfaceName();
+                String interfaceName = interfaceInfoOp.get().getInterfaceName();
                 ElanTagName elanTagName = ElanUtils.getElanInfoByElanTag(elanTag);
+                if (elanTagName == null) {
+                    logger.warn("not able to find elanTagName in elan-tag-name-map for elan tag {}", elanTag);
+                    return;
+                }
                 String elanName = elanTagName.getName();
-                Elan elanInfo = ElanUtils.getElanByName(elanName);
                 MacEntry macEntry = ElanUtils.getInterfaceMacEntriesOperationalDataPath(interfaceName, physAddress);
                 if(macEntry != null && macEntry.getInterface() == interfaceName) {
                     BigInteger macTimeStamp = macEntry.getControllerLearnedForwardingEntryTimestamp();
@@ -122,6 +132,9 @@ public class ElanPacketInHandler implements PacketProcessingListener {
                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, elanMacEntryId, macEntry);
                 ElanInstance elanInstance = ElanUtils.getElanInstanceByName(elanName);
                 ElanUtils.setupMacFlows(elanInstance, interfaceManager.getInterfaceInfo(interfaceName), elanInstance.getMacTimeout(), macAddress);
+
+                BigInteger dpId = interfaceManager.getDpnForInterface(interfaceName);
+                ElanL2GatewayUtils.installMacsInElanExternalDevices(elanInstance, dpId, Arrays.asList(physAddress));
             } catch (Exception e) {
                 logger.trace("Failed to decode packet: {}", e);
             }
@@ -150,6 +163,7 @@ public class ElanPacketInHandler implements PacketProcessingListener {
             return;
         }
         ElanUtils.deleteMacFlows(elanInfo, oldInterfaceLport, macEntry);
+        ElanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInfo, Arrays.asList(macEntry.getMacAddress()));
     }
 
 }
index e1fafb55b966b1c934a7034d8f45f0d7acb97576..af817b8595fd34ea60aef5f3c6d60f3a40072efa 100644 (file)
@@ -5,25 +5,40 @@
  * 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.vpnservice.elan.internal;
 
-import com.google.common.base.Optional;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Future;
+
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
 import org.opendaylight.elanmanager.api.IElanService;
+import org.opendaylight.elanmanager.exceptions.MacNotFoundException;
+import org.opendaylight.vpnservice.elan.l2gw.internal.ElanL2GatewayProvider;
 import org.opendaylight.vpnservice.elan.statisitcs.ElanStatisticsImpl;
+import org.opendaylight.vpnservice.elan.statusanddiag.ElanStatusMonitor;
 import org.opendaylight.vpnservice.elan.utils.ElanConstants;
 import org.opendaylight.vpnservice.elan.utils.ElanUtils;
 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
 import org.opendaylight.vpnservice.itm.api.IITMProvider;
 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
 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.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LocalUcastMacs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteUcastMacs;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanInstances;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac;
@@ -41,16 +56,16 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.elanmanager.exceptions.MacNotFoundException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Future;
+import com.google.common.base.Optional;
 
 public class ElanServiceProvider implements BindingAwareProvider, IElanService, AutoCloseable {
 
@@ -67,68 +82,87 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
     private ElanNodeListener elanNodeListener;
     private NotificationService notificationService;
     private RpcProviderRegistry rpcProviderRegistry;
+    private IITMProvider itmManager;
+    private ItmRpcService itmRpcService;
+    private DataBroker broker;
+    private ElanL2GatewayProvider elanL2GatewayProvider;
+
+    private EntityOwnershipService entityOwnershipService;
+    private BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer;
+
+    private static final ElanStatusMonitor elanStatusMonitor = ElanStatusMonitor.getInstance();
 
     public ElanServiceProvider(RpcProviderRegistry rpcRegistry) {
         rpcProviderRegistry = rpcRegistry;
+        elanStatusMonitor.registerMbean();
     }
 
-    //private ElanInterfaceStateChangeListener elanInterfaceEventListener;
+    // private ElanInterfaceStateChangeListener elanInterfaceEventListener;
     private ElanItmEventListener elanItmEventListener;
 
-    public void setItmRpcService(ItmRpcService itmRpcService) {
-        this.itmRpcService = itmRpcService;
-    }
-
-    public ItmRpcService getItmRpcService() {
-        return itmRpcService;
-    }
-
-    private ItmRpcService itmRpcService;
-    private DataBroker broker;
-
     private static final Logger logger = LoggerFactory.getLogger(ElanServiceProvider.class);
 
     @Override
     public void onSessionInitiated(ProviderContext session) {
-        createIdPool();
-        broker = session.getSALService(DataBroker.class);
+        elanStatusMonitor.reportStatus("STARTING");
+        try {
+            createIdPool();
+            broker = session.getSALService(DataBroker.class);
+
+            ElanUtils.setDataBroker(broker);
+            ElanUtils.setIfaceMgrRpcService(interfaceManagerRpcService);
+            ElanUtils.setItmRpcService(itmRpcService);
+            ElanUtils.setMdsalManager(mdsalManager);
+
+            elanForwardingEntriesHandler = new ElanForwardingEntriesHandler(broker);
+
+            elanInterfaceManager = ElanInterfaceManager.getElanInterfaceManager();
+            elanInterfaceManager.setInterfaceManager(interfaceManager);
+            elanInterfaceManager.setIdManager(idManager);
+            elanInterfaceManager.setMdSalApiManager(mdsalManager);
+            elanInterfaceManager.setDataBroker(broker);
+            elanInterfaceManager.setInterfaceManagerRpcService(interfaceManagerRpcService);
+            elanInterfaceManager.setElanForwardingEntriesHandler(elanForwardingEntriesHandler);
+
+            elanInstanceManager = ElanInstanceManager.getElanInstanceManager();
+            elanInstanceManager.setDataBroker(broker);
+            elanInstanceManager.setIdManager(idManager);
+            elanInstanceManager.setElanInterfaceManager(elanInterfaceManager);
+
+
+            elanNodeListener = new ElanNodeListener(broker, mdsalManager);
+
+            elanPacketInHandler = new ElanPacketInHandler(broker);
+            elanPacketInHandler.setInterfaceManager(interfaceManager);
+
 
-        elanForwardingEntriesHandler = new ElanForwardingEntriesHandler(broker);
+            elanSmacFlowEventListener = new ElanSmacFlowEventListener(broker);
+            elanSmacFlowEventListener.setMdSalApiManager(mdsalManager);
+            elanSmacFlowEventListener.setInterfaceManager(interfaceManager);
+            elanSmacFlowEventListener.setSalFlowService(session.getRpcService(SalFlowService.class));
 
-        elanInterfaceManager = ElanInterfaceManager.getElanInterfaceManager();
-        elanInterfaceManager.setInterfaceManager(interfaceManager);
-        elanInterfaceManager.setIdManager(idManager);
-        elanInterfaceManager.setMdSalApiManager(mdsalManager);
-        elanInterfaceManager.setDataBroker(broker);
-        elanInterfaceManager.registerListener();
-        elanInterfaceManager.setInterfaceManagerRpcService(interfaceManagerRpcService);
-        elanInterfaceManager.setElanForwardingEntriesHandler(elanForwardingEntriesHandler);
 
-        elanInstanceManager = ElanInstanceManager.getElanInstanceManager();
-        elanInstanceManager.setDataBroker(broker);
-        elanInstanceManager.setIdManager(idManager);
-        elanInstanceManager.setElanInterfaceManager(elanInterfaceManager);
-        elanInstanceManager.registerListener();
+            // Initialize statistics rpc provider for elan
+            ElanStatisticsService interfaceStatsService = new ElanStatisticsImpl(broker, interfaceManager,
+                    mdsalManager);
+            rpcProviderRegistry.addRpcImplementation(ElanStatisticsService.class, interfaceStatsService);
 
-        elanNodeListener = new ElanNodeListener(broker, mdsalManager);
+            elanInterfaceStateChangeListener = new ElanInterfaceStateChangeListener(broker, elanInterfaceManager);
+            elanInterfaceStateChangeListener.setInterfaceManager(interfaceManager);
 
-        elanPacketInHandler = new ElanPacketInHandler(broker);
-        elanPacketInHandler.setInterfaceManager(interfaceManager);
-        notificationService.registerNotificationListener(elanPacketInHandler);
 
-        elanSmacFlowEventListener = new ElanSmacFlowEventListener(broker);
-        elanSmacFlowEventListener.setMdSalApiManager(mdsalManager);
-        elanSmacFlowEventListener.setInterfaceManager(interfaceManager);
-        elanSmacFlowEventListener.setSalFlowService(session.getRpcService(SalFlowService.class));
-        notificationService.registerNotificationListener(elanSmacFlowEventListener);
+            this.elanL2GatewayProvider = new ElanL2GatewayProvider(this);
 
-        // Initialize statistics rpc provider for elan
-        ElanStatisticsService interfaceStatsService = new ElanStatisticsImpl(broker, interfaceManager, mdsalManager);
-        rpcProviderRegistry.addRpcImplementation(ElanStatisticsService.class, interfaceStatsService);
+            elanInterfaceManager.registerListener();
+            elanInstanceManager.registerListener();
+            notificationService.registerNotificationListener(elanSmacFlowEventListener);
+            notificationService.registerNotificationListener(elanPacketInHandler);
 
-        elanInterfaceStateChangeListener = new ElanInterfaceStateChangeListener(broker, elanInterfaceManager);
-        elanInterfaceStateChangeListener.setInterfaceManager(interfaceManager);
-        ElanUtils.setElanServiceProvider(this);
+            elanStatusMonitor.reportStatus("OPERATIONAL");
+        } catch (Exception e) {
+            logger.error("Error initializing services", e);
+            elanStatusMonitor.reportStatus("ERROR");
+        }
     }
 
     public void setIdManager(IdManagerService idManager) {
@@ -143,11 +177,27 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
         this.interfaceManager = interfaceManager;
     }
 
+    public void setEntityOwnershipService(EntityOwnershipService entityOwnershipService) {
+        this.entityOwnershipService = entityOwnershipService;
+    }
+
+    public void setBindingNormalizedNodeSerializer(BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer) {
+        this.bindingNormalizedNodeSerializer = bindingNormalizedNodeSerializer;
+    }
+
+    public IInterfaceManager getInterfaceManager() {
+        return this.interfaceManager;
+    }
+
     public IMdsalApiManager getMdsalManager() {
         return mdsalManager;
     }
 
-     public DataBroker getBroker() {
+    public IITMProvider getItmManager() {
+        return itmManager;
+    }
+
+    public DataBroker getBroker() {
         return broker;
     }
 
@@ -163,14 +213,41 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
         return interfaceManagerRpcService;
     }
 
+    public void setItmManager(IITMProvider itmManager) {
+        this.itmManager = itmManager;
+    }
+
+    public void setItmRpcService(ItmRpcService itmRpcService) {
+        this.itmRpcService = itmRpcService;
+    }
+
+    public ItmRpcService getItmRpcService() {
+        return itmRpcService;
+    }
+
+    public ElanInstanceManager getElanInstanceManager() {
+        return elanInstanceManager;
+    }
+
+    public ElanInterfaceManager getElanInterfaceManager() {
+        return elanInterfaceManager;
+    }
+
+    public EntityOwnershipService getEntityOwnershipService() {
+        return entityOwnershipService;
+    }
+
+    public BindingNormalizedNodeSerializer getBindingNormalizedNodeSerializer() {
+        return bindingNormalizedNodeSerializer;
+    }
+
     private void createIdPool() {
-        CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
-            .setPoolName(ElanConstants.ELAN_ID_POOL_NAME).setLow(ElanConstants.ELAN_ID_LOW_VALUE).setHigh(ElanConstants.ELAN_ID_HIGH_VALUE)
-            .build();
+        CreateIdPoolInput createPool = new CreateIdPoolInputBuilder().setPoolName(ElanConstants.ELAN_ID_POOL_NAME)
+                .setLow(ElanConstants.ELAN_ID_LOW_VALUE).setHigh(ElanConstants.ELAN_ID_HIGH_VALUE).build();
         try {
-           Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
-           if ((result != null) && (result.get().isSuccessful())) {
-               logger.debug("ELAN Id Pool is created successfully");
+            Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
+            if ((result != null) && (result.get().isSuccessful())) {
+                logger.debug("ELAN Id Pool is created successfully");
             }
         } catch (Exception e) {
             logger.error("Failed to create ELAN Id pool {}", e);
@@ -181,30 +258,40 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
     public boolean createElanInstance(String elanInstanceName, long macTimeout, String description) {
         ElanInstance existingElanInstance = elanInstanceManager.getElanInstanceByName(elanInstanceName);
         boolean isSuccess = true;
-        if(existingElanInstance != null) {
-           if(compareWithExistingElanInstance(existingElanInstance, macTimeout, description)) {
-               logger.debug("Elan Instance is already present in the Operational DS {}", existingElanInstance);
-               return true;
-           } else {
-               ElanInstance updateElanInstance = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName).setDescription(description).setMacTimeout(macTimeout).setKey(new ElanInstanceKey(elanInstanceName)).build();
-               MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInstanceConfigurationDataPath(elanInstanceName), updateElanInstance);
-               logger.debug("Updating the Elan Instance {} with MAC TIME-OUT %l and Description %s ", updateElanInstance, macTimeout, description);
-           }
+        if (existingElanInstance != null) {
+            if (compareWithExistingElanInstance(existingElanInstance, macTimeout, description)) {
+                logger.debug("Elan Instance is already present in the Operational DS {}", existingElanInstance);
+                return true;
+            } else {
+                ElanInstance updateElanInstance = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName)
+                        .setDescription(description).setMacTimeout(macTimeout)
+                        .setKey(new ElanInstanceKey(elanInstanceName)).build();
+                MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
+                        ElanUtils.getElanInstanceConfigurationDataPath(elanInstanceName), updateElanInstance);
+                logger.debug("Updating the Elan Instance {} with MAC TIME-OUT %l and Description %s ",
+                        updateElanInstance, macTimeout, description);
+            }
         } else {
-            ElanInstance elanInstance = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName).setMacTimeout(macTimeout).setDescription(description).setKey(new ElanInstanceKey(elanInstanceName)).build();
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInstanceConfigurationDataPath(elanInstanceName), elanInstance);
+            ElanInstance elanInstance = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName)
+                    .setMacTimeout(macTimeout).setDescription(description).setKey(new ElanInstanceKey(elanInstanceName))
+                    .build();
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
+                    ElanUtils.getElanInstanceConfigurationDataPath(elanInstanceName), elanInstance);
             logger.debug("Creating the new Elan Instance {}", elanInstance);
         }
         return isSuccess;
     }
 
-    public static boolean compareWithExistingElanInstance(ElanInstance existingElanInstance, long macTimeOut, String description) {
+    public static boolean compareWithExistingElanInstance(ElanInstance existingElanInstance, long macTimeOut,
+            String description) {
         boolean isEqual = false;
-        if(existingElanInstance.getMacTimeout() == macTimeOut && existingElanInstance.getDescription().equals(description)) {
+        if (existingElanInstance.getMacTimeout() == macTimeOut
+                && existingElanInstance.getDescription().equals(description)) {
             isEqual = true;
         }
         return isEqual;
     }
+
     @Override
     public void updateElanInstance(String elanInstanceName, long newMacTimout, String newDescription) {
         createElanInstance(elanInstanceName, newMacTimout, newDescription);
@@ -214,34 +301,43 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
     public boolean deleteElanInstance(String elanInstanceName) {
         boolean isSuccess = false;
         ElanInstance existingElanInstance = elanInstanceManager.getElanInstanceByName(elanInstanceName);
-        if(existingElanInstance == null) {
-            logger.debug("Elan Instance is not present {}" , existingElanInstance);
+        if (existingElanInstance == null) {
+            logger.debug("Elan Instance is not present {}", existingElanInstance);
             return isSuccess;
         }
         logger.debug("Deletion of the existing Elan Instance {}", existingElanInstance);
-        ElanUtils.delete(broker, LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInstanceConfigurationDataPath(elanInstanceName));
+        ElanUtils.delete(broker, LogicalDatastoreType.CONFIGURATION,
+                ElanUtils.getElanInstanceConfigurationDataPath(elanInstanceName));
         isSuccess = true;
         return isSuccess;
     }
 
     @Override
-    public void addElanInterface(String elanInstanceName, String interfaceName, List<String> staticMacAddresses, String description) {
-          ElanInstance existingElanInstance = elanInstanceManager.getElanInstanceByName(elanInstanceName);
-          if(existingElanInstance != null) {
-              ElanInterface elanInterface;
-              if(staticMacAddresses == null) {
-                  elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName).setDescription(description).setName(interfaceName).setKey(new ElanInterfaceKey(interfaceName)).build();
-              } else {
-                  elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName).setDescription(description).setName(interfaceName).setStaticMacEntries(getPhysAddress(staticMacAddresses)).setKey(new ElanInterfaceKey(interfaceName)).build();
-              }
-              MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName), elanInterface);
-              logger.debug("Creating the new ELan Interface {}", elanInterface);
-          }
+    public void addElanInterface(String elanInstanceName, String interfaceName, List<String> staticMacAddresses,
+            String description) {
+        ElanInstance existingElanInstance = elanInstanceManager.getElanInstanceByName(elanInstanceName);
+        if (existingElanInstance != null) {
+            ElanInterface elanInterface;
+            if (staticMacAddresses == null) {
+                elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
+                        .setDescription(description).setName(interfaceName).setKey(new ElanInterfaceKey(interfaceName))
+                        .build();
+            } else {
+                elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
+                        .setDescription(description).setName(interfaceName)
+                        .setStaticMacEntries(getPhysAddress(staticMacAddresses))
+                        .setKey(new ElanInterfaceKey(interfaceName)).build();
+            }
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
+                    ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName), elanInterface);
+            logger.debug("Creating the new ELan Interface {}", elanInterface);
+        }
 
     }
 
     @Override
-    public void updateElanInterface(String elanInstanceName, String interfaceName, List<String> updatedStaticMacAddresses, String newDescription) {
+    public void updateElanInterface(String elanInstanceName, String interfaceName,
+            List<String> updatedStaticMacAddresses, String newDescription) {
         ElanInterface existingElanInterface = ElanUtils.getElanInterfaceByElanInterfaceName(interfaceName);
         if (existingElanInterface == null) {
             return;
@@ -249,18 +345,22 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
         List<PhysAddress> existingMacAddress = existingElanInterface.getStaticMacEntries();
         List<PhysAddress> updatedMacAddresses = getPhysAddress(updatedStaticMacAddresses);
         List<PhysAddress> updatedPhysAddress = getUpdatedPhyAddress(existingMacAddress, updatedMacAddresses);
-        if(updatedPhysAddress.size() > 0) {
+        if (updatedPhysAddress.size() > 0) {
             logger.debug("updating the ElanInterface with new Mac Entries {}", updatedStaticMacAddresses);
-            ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName).setName(interfaceName).setDescription(newDescription).setStaticMacEntries(updatedPhysAddress).setKey(new ElanInterfaceKey(interfaceName)).build();
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName), elanInterface);
+            ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
+                    .setName(interfaceName).setDescription(newDescription).setStaticMacEntries(updatedPhysAddress)
+                    .setKey(new ElanInterfaceKey(interfaceName)).build();
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
+                    ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName), elanInterface);
         }
     }
 
     @Override
     public void deleteElanInterface(String elanInstanceName, String interfaceName) {
         ElanInterface existingElanInterface = ElanUtils.getElanInterfaceByElanInterfaceName(interfaceName);
-        if(existingElanInterface != null) {
-            ElanUtils.delete(broker, LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName));
+        if (existingElanInterface != null) {
+            ElanUtils.delete(broker, LogicalDatastoreType.CONFIGURATION,
+                    ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName));
             logger.debug("deleting the Elan Interface {}", existingElanInterface);
         }
     }
@@ -271,27 +371,36 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
         PhysAddress updateStaticMacAddress = new PhysAddress(macAddress);
         if (existingElanInterface != null) {
             List<PhysAddress> existingMacAddress = existingElanInterface.getStaticMacEntries();
-            if(existingMacAddress.contains(updateStaticMacAddress)) {
+            if (existingMacAddress.contains(updateStaticMacAddress)) {
                 return;
             }
             existingMacAddress.add(updateStaticMacAddress);
-            ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName).setName(interfaceName).setStaticMacEntries(existingMacAddress).setDescription(existingElanInterface.getDescription()).setKey(new ElanInterfaceKey(interfaceName)).build();
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName), elanInterface);
+            ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
+                    .setName(interfaceName).setStaticMacEntries(existingMacAddress)
+                    .setDescription(existingElanInterface.getDescription()).setKey(new ElanInterfaceKey(interfaceName))
+                    .build();
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
+                    ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName), elanInterface);
         }
     }
 
     @Override
-    public void deleteStaticMacAddress(String elanInstanceName, String interfaceName, String macAddress) throws MacNotFoundException {
-           ElanInterface existingElanInterface = ElanUtils.getElanInterfaceByElanInterfaceName(interfaceName);
+    public void deleteStaticMacAddress(String elanInstanceName, String interfaceName, String macAddress)
+            throws MacNotFoundException {
+        ElanInterface existingElanInterface = ElanUtils.getElanInterfaceByElanInterfaceName(interfaceName);
         PhysAddress physAddress = new PhysAddress(macAddress);
-        if(existingElanInterface == null) {
+        if (existingElanInterface == null) {
             return;
         }
         List<PhysAddress> existingMacAddress = existingElanInterface.getStaticMacEntries();
-        if(existingMacAddress.contains(physAddress)) {
+        if (existingMacAddress.contains(physAddress)) {
             existingMacAddress.remove(physAddress);
-            ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName).setName(interfaceName).setStaticMacEntries(existingMacAddress).setDescription(existingElanInterface.getDescription()).setKey(new ElanInterfaceKey(interfaceName)).build();
-            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName), elanInterface);
+            ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
+                    .setName(interfaceName).setStaticMacEntries(existingMacAddress)
+                    .setDescription(existingElanInterface.getDescription()).setKey(new ElanInterfaceKey(interfaceName))
+                    .build();
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
+                    ElanUtils.getElanInterfaceConfigurationDataPathId(interfaceName), elanInterface);
         } else {
             throw new MacNotFoundException("Mac Not Found Exception");
         }
@@ -301,25 +410,26 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
     public Collection<MacEntry> getElanMacTable(String elanInstanceName) {
         Elan elanInfo = ElanUtils.getElanByName(elanInstanceName);
         List<MacEntry> macAddress = new ArrayList<>();
-        if(elanInfo == null) {
+        if (elanInfo == null) {
             return macAddress;
         }
-       List<String> elanInterfaces =  elanInfo.getElanInterfaces();
-        if(elanInterfaces != null && elanInterfaces.size() > 0) {
-            for(String elanInterface : elanInterfaces) {
+        List<String> elanInterfaces = elanInfo.getElanInterfaces();
+        if (elanInterfaces != null && elanInterfaces.size() > 0) {
+            for (String elanInterface : elanInterfaces) {
                 ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(elanInterface);
-                if(elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null && elanInterfaceMac.getMacEntry().size() > 0){
+                if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null
+                        && elanInterfaceMac.getMacEntry().size() > 0) {
                     macAddress.addAll(elanInterfaceMac.getMacEntry());
                 }
             }
         }
-       return macAddress;
+        return macAddress;
     }
 
     @Override
     public void flushMACTable(String elanInstanceName) {
         Elan elanInfo = ElanUtils.getElanByName(elanInstanceName);
-        if(elanInfo == null) {
+        if (elanInfo == null) {
             return;
         }
         List<String> elanInterfaces = elanInfo.getElanInterfaces();
@@ -329,8 +439,8 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
         for (String elanInterface : elanInterfaces) {
             ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(elanInterface);
             if (elanInterfaceMac.getMacEntry() != null && elanInterfaceMac.getMacEntry().size() > 0) {
-                List<MacEntry> macEntries =  elanInterfaceMac.getMacEntry();
-                for(MacEntry macEntry : macEntries) {
+                List<MacEntry> macEntries = elanInterfaceMac.getMacEntry();
+                for (MacEntry macEntry : macEntries) {
                     try {
                         deleteStaticMacAddress(elanInstanceName, elanInterface, macEntry.getMacAddress().getValue());
                     } catch (MacNotFoundException e) {
@@ -345,20 +455,21 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
 
     @Override
     public void close() throws Exception {
-        elanInstanceManager.close();
+        this.elanInstanceManager.close();
+        this.elanL2GatewayProvider.close();
     }
 
     public static List<PhysAddress> getPhysAddress(List<String> macAddress) {
         List<PhysAddress> physAddresses = new ArrayList<>();
-        for(String mac : macAddress) {
+        for (String mac : macAddress) {
             physAddresses.add(new PhysAddress(mac));
         }
         return physAddresses;
     }
 
-
-    public List<PhysAddress> getUpdatedPhyAddress(List<PhysAddress> originalAddresses, List<PhysAddress> updatePhyAddresses) {
-        if(updatePhyAddresses != null && !updatePhyAddresses.isEmpty()) {
+    public List<PhysAddress> getUpdatedPhyAddress(List<PhysAddress> originalAddresses,
+            List<PhysAddress> updatePhyAddresses) {
+        if (updatePhyAddresses != null && !updatePhyAddresses.isEmpty()) {
             List<PhysAddress> existingClonedPhyAddress = new ArrayList<>();
             if (originalAddresses != null && !originalAddresses.isEmpty()) {
                 existingClonedPhyAddress.addAll(0, originalAddresses);
@@ -377,9 +488,11 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
     @Override
     public List<ElanInstance> getElanInstances() {
         List<ElanInstance> elanList = new ArrayList<ElanInstance>();
-        InstanceIdentifier<ElanInstances> elanInstancesIdentifier =  InstanceIdentifier.builder(ElanInstances.class).build();
-        Optional<ElanInstances> elansOptional  = ElanUtils.read(broker, LogicalDatastoreType.CONFIGURATION, elanInstancesIdentifier);
-        if(elansOptional.isPresent()) {
+        InstanceIdentifier<ElanInstances> elanInstancesIdentifier = InstanceIdentifier.builder(ElanInstances.class)
+                .build();
+        Optional<ElanInstances> elansOptional = ElanUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
+                elanInstancesIdentifier);
+        if (elansOptional.isPresent()) {
             elanList.addAll(elansOptional.get().getElanInstance());
         }
         return elanList;
@@ -388,14 +501,16 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService,
     @Override
     public List<String> getElanInterfaces(String elanInstanceName) {
         List<String> elanInterfaces = new ArrayList<>();
-        InstanceIdentifier<ElanInterfaces> elanInterfacesIdentifier =  InstanceIdentifier.builder(ElanInterfaces.class).build();
-        Optional<ElanInterfaces> elanInterfacesOptional  = ElanUtils.read(broker, LogicalDatastoreType.CONFIGURATION, elanInterfacesIdentifier);
-        if(!elanInterfacesOptional.isPresent()) {
-             return elanInterfaces;
+        InstanceIdentifier<ElanInterfaces> elanInterfacesIdentifier = InstanceIdentifier.builder(ElanInterfaces.class)
+                .build();
+        Optional<ElanInterfaces> elanInterfacesOptional = ElanUtils.read(broker, LogicalDatastoreType.CONFIGURATION,
+                elanInterfacesIdentifier);
+        if (!elanInterfacesOptional.isPresent()) {
+            return elanInterfaces;
         }
         List<ElanInterface> elanInterfaceList = elanInterfacesOptional.get().getElanInterface();
-        for(ElanInterface elanInterface : elanInterfaceList) {
-            if(elanInterface.getElanInstanceName().equals(elanInstanceName)) {
+        for (ElanInterface elanInterface : elanInterfaceList) {
+            if (elanInterface.getElanInstanceName().equals(elanInstanceName)) {
                 elanInterfaces.add(elanInterface.getName());
             }
         }
index 9ea543e3473230650fd328aa8e5846b45b1747e8..d8ff705d101ee26b644e206da5450a16556f017c 100644 (file)
@@ -7,30 +7,41 @@
  */
 package org.opendaylight.vpnservice.elan.internal;
 
+import java.math.BigInteger;
+
 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.vpnservice.elan.utils.ElanConstants;
 import org.opendaylight.vpnservice.elan.utils.ElanUtils;
 import org.opendaylight.vpnservice.interfacemgr.globals.InterfaceInfo;
 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
-import org.opendaylight.vpnservice.itm.api.IITMProvider;
 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
 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.flow.service.rev130819.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.tag.name.map.ElanTagName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.forwarding.entries.MacEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007._if.indexes._interface.map.IfIndexInterface;
-//import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331._if.indexes._interface.map.IfIndexInterface;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.math.BigInteger;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 
+@SuppressWarnings("deprecation")
 public class ElanSmacFlowEventListener implements SalFlowListener {
     private final DataBroker broker;
-    private IMdsalApiManager mdsalManager;
     private IInterfaceManager interfaceManager;
     private static final Logger logger = LoggerFactory.getLogger(ElanSmacFlowEventListener.class);
 
@@ -52,7 +63,6 @@ public class ElanSmacFlowEventListener implements SalFlowListener {
 
 
     public void setMdSalApiManager(IMdsalApiManager mdsalManager) {
-        this.mdsalManager = mdsalManager;
     }
     @Override
     public void onFlowAdded(FlowAdded arg0) {
@@ -94,15 +104,19 @@ public class ElanSmacFlowEventListener implements SalFlowListener {
             if (elanTagInfo == null) {
                 return;
             }
-            String srcMacAddress = switchFlowRemoved.getMatch().getEthernetMatch()
+            final String srcMacAddress = switchFlowRemoved.getMatch().getEthernetMatch()
                     .getEthernetSource().getAddress().getValue().toUpperCase();
             int portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
             if (portTag == 0) {
                 logger.debug(String.format("Flow removed event on SMAC flow entry. But having port Tag as 0 "));
                 return;
             }
-            IfIndexInterface existingInterfaceInfo = ElanUtils.getInterfaceInfoByInterfaceTag(portTag);
-            String interfaceName = existingInterfaceInfo.getInterfaceName();
+            Optional<IfIndexInterface> existingInterfaceInfo = ElanUtils.getInterfaceInfoByInterfaceTag(portTag);
+            if (!existingInterfaceInfo.isPresent()) {
+                logger.debug("Interface is not available for port Tag {}", portTag);
+                return;
+            }
+            String interfaceName = existingInterfaceInfo.get().getInterfaceName();
             PhysAddress physAddress = new PhysAddress(srcMacAddress);
             if (interfaceName == null) {
                 logger.error(String.format("LPort record not found for tag %d", portTag));
@@ -113,9 +127,25 @@ public class ElanSmacFlowEventListener implements SalFlowListener {
             if(macEntry != null && interfaceInfo != null) {
                 ElanUtils.deleteMacFlows(ElanUtils.getElanInstanceByName(elanTagInfo.getName()), interfaceInfo, macEntry);
             }
-            InstanceIdentifier<MacEntry> macEntryId =  ElanUtils.getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
-            ElanUtils.delete(broker, LogicalDatastoreType.OPERATIONAL, macEntryId);
+            InstanceIdentifier<MacEntry> macEntryIdForElanInterface =  ElanUtils.getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
+            InstanceIdentifier<MacEntry> macEntryIdForElanInstance  =  ElanUtils.getMacEntryOperationalDataPath(elanTagInfo.getName(), physAddress);
+            WriteTransaction tx = broker.newWriteOnlyTransaction();
+            tx.delete(LogicalDatastoreType.OPERATIONAL, macEntryIdForElanInterface);
+            tx.delete(LogicalDatastoreType.OPERATIONAL, macEntryIdForElanInstance);
+            ListenableFuture<Void> writeResult = tx.submit();
+
+            //WRITE Callback
+            Futures.addCallback(writeResult, new FutureCallback<Void>() {
+                @Override
+                public void onSuccess(Void noarg) {
+                    logger.debug("Successfully removed macEntry {} from Operational Datastore", srcMacAddress);
+                }
+
+                @Override
+                public void onFailure(Throwable error) {
+                    logger.debug("Error {} while removing macEntry {} from Operational Datastore", error, srcMacAddress);
+                }
+            });
         }
     }
-
 }
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/internal/ElanL2GatewayProvider.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/internal/ElanL2GatewayProvider.java
new file mode 100644 (file)
index 0000000..76bdb6e
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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.vpnservice.elan.l2gw.internal;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
+import org.opendaylight.vpnservice.elan.internal.ElanInstanceManager;
+import org.opendaylight.vpnservice.elan.internal.ElanInterfaceManager;
+import org.opendaylight.vpnservice.elan.internal.ElanServiceProvider;
+import org.opendaylight.vpnservice.elan.l2gw.listeners.HwvtepLocalUcastMacListener;
+import org.opendaylight.vpnservice.elan.l2gw.listeners.HwvtepNodeListener;
+import org.opendaylight.vpnservice.elan.l2gw.listeners.L2GatewayConnectionListener;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayMulticastUtils;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Elan L2 Gateway provider class.
+ */
+public class ElanL2GatewayProvider implements AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ElanL2GatewayProvider.class);
+
+    private DataBroker broker;
+    private EntityOwnershipService entityOwnershipService;
+    private BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer;
+    private ItmRpcService itmRpcService;
+    private ElanInstanceManager elanInstanceManager;
+    private ElanInterfaceManager elanInterfaceManager;
+
+    private L2GatewayConnectionListener l2GwConnListener;
+    private HwvtepNodeListener hwvtepNodeListener;
+    private HwvtepLocalUcastMacListener torMacsListener;
+
+    /**
+     * Instantiates a new elan l2 gateway provider.
+     *
+     * @param elanServiceProvider
+     *            the elan service provider
+     */
+    public ElanL2GatewayProvider(ElanServiceProvider elanServiceProvider) {
+        this.broker = elanServiceProvider.getBroker();
+        this.entityOwnershipService = elanServiceProvider.getEntityOwnershipService();
+        this.bindingNormalizedNodeSerializer = elanServiceProvider.getBindingNormalizedNodeSerializer();
+        this.itmRpcService = elanServiceProvider.getItmRpcService();
+        this.elanInstanceManager = elanServiceProvider.getElanInstanceManager();
+        this.elanInterfaceManager = elanServiceProvider.getElanInterfaceManager();
+
+        init();
+
+        LOG.info("ElanL2GatewayProvider Initialized");
+    }
+
+    /**
+     * Initialize Elan L2 Gateway.
+     */
+    private void init() {
+        ElanL2GwCacheUtils.createElanL2GwDeviceCache();
+        ElanL2GatewayUtils.setDataBroker(broker);
+        ElanL2GatewayUtils.setItmRpcService(itmRpcService);
+
+        ElanL2GatewayMulticastUtils.setBroker(broker);
+        ElanL2GatewayMulticastUtils.setElanInstanceManager(elanInstanceManager);
+        ElanL2GatewayMulticastUtils.setElanInterfaceManager(elanInterfaceManager);
+
+        this.torMacsListener = new HwvtepLocalUcastMacListener(broker, entityOwnershipService,
+                bindingNormalizedNodeSerializer);
+        this.l2GwConnListener = new L2GatewayConnectionListener(broker, entityOwnershipService,
+                bindingNormalizedNodeSerializer, elanInstanceManager);
+        this.hwvtepNodeListener = new HwvtepNodeListener(broker, entityOwnershipService,
+                bindingNormalizedNodeSerializer, elanInstanceManager, itmRpcService);
+        this.hwvtepNodeListener.registerListener(LogicalDatastoreType.OPERATIONAL, broker);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.AutoCloseable#close()
+     */
+    @Override
+    public void close() throws Exception {
+        this.torMacsListener.close();
+        this.l2GwConnListener.close();
+        this.hwvtepNodeListener.close();
+        LOG.info("ElanL2GatewayProvider Closed");
+    }
+}
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLocalUcastMacListener.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLocalUcastMacListener.java
new file mode 100644 (file)
index 0000000..c2558fe
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * 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.vpnservice.elan.l2gw.listeners;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LocalUcastMacs;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.instances.ElanInstance;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayUtils;
+import org.opendaylight.vpnservice.datastoreutils.AsyncClusteredDataChangeListenerBase;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepUtils;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A listener for Ucast MAC entries that are added/removed to/from an External Device (e.g., TOR).
+ *
+ * When a Ucast MAC addr appears in the hwvtep's operational DS, that MAC must be populated in DMAC tables in all
+ * Elan participating DPNs. ELAN is selected according to field 'tunnel_key' of the Logical Switch to which the new
+ * MAC belongs.
+ *
+ */
+public class HwvtepLocalUcastMacListener extends
+        AsyncClusteredDataChangeListenerBase<LocalUcastMacs, HwvtepLocalUcastMacListener> implements AutoCloseable {
+
+    private DataBroker broker;
+    private EntityOwnershipService entityOwnershipService;
+    private BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer;
+    private ListenerRegistration<DataChangeListener> lstnerRegistration;
+
+    private static final Logger logger = LoggerFactory.getLogger(HwvtepLocalUcastMacListener.class);
+
+    public HwvtepLocalUcastMacListener(DataBroker broker, EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer) {
+        super(LocalUcastMacs.class, HwvtepLocalUcastMacListener.class);
+
+        this.broker = broker;
+        this.entityOwnershipService = entityOwnershipService;
+        this.bindingNormalizedNodeSerializer = bindingNormalizedNodeSerializer;
+        registerListener();
+    }
+
+    protected void registerListener() {
+        try {
+            lstnerRegistration = this.broker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
+                    HwvtepUtils.getWildCardPathForLocalUcastMacs(), this, DataChangeScope.SUBTREE);
+        } catch (final Exception e) {
+            logger.error("Hwvtep LocalUcasMacs DataChange listener registration failed !", e);
+            throw new IllegalStateException("Hwvtep LocalUcasMacs DataChange listener registration failed .", e);
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (lstnerRegistration != null) {
+            try {
+                lstnerRegistration.close();
+            } catch (final Exception e) {
+                logger.error("Error when cleaning up DataChangeListener.", e);
+            }
+            lstnerRegistration = null;
+        }
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<LocalUcastMacs> identifier, LocalUcastMacs macRemoved) {
+        String hwvtepNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
+        String macAddress = macRemoved.getMacEntryKey().getValue();
+
+        logger.trace("LocalUcastMacs {} removed from {}", macAddress, hwvtepNodeId);
+
+        ElanInstance elan = ElanL2GatewayUtils.getElanInstanceForUcastLocalMac(macRemoved);
+        if (elan == null) {
+            logger.warn("Could not find ELAN for mac {} being deleted", macAddress);
+            return;
+        }
+
+        String elanName = elan.getElanInstanceName();
+        L2GatewayDevice elanL2GwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName, hwvtepNodeId);
+        if (elanL2GwDevice == null) {
+            logger.warn("Could not find L2GatewayDevice for ELAN: {}, nodeID:{} from cache", elanName, hwvtepNodeId);
+            return;
+        }
+
+        // Remove MAC from cache
+        elanL2GwDevice.removeUcastLocalMac(macRemoved);
+
+        ElanL2GatewayUtils.unInstallL2GwUcastMacFromElan(entityOwnershipService, bindingNormalizedNodeSerializer, elan,
+                elanL2GwDevice, macRemoved);    }
+
+    @Override
+    protected void update(InstanceIdentifier<LocalUcastMacs> identifier, LocalUcastMacs original,
+            LocalUcastMacs update) {
+        // TODO (eperefr) what can change here?
+
+    }
+
+    @Override
+    protected void add(InstanceIdentifier<LocalUcastMacs> identifier, LocalUcastMacs macAdded) {
+        String hwvtepNodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue();
+        String macAddress = macAdded.getMacEntryKey().getValue();
+
+        logger.trace("LocalUcastMacs {} added to {}", macAddress, hwvtepNodeId);
+
+        ElanInstance elan = ElanL2GatewayUtils.getElanInstanceForUcastLocalMac(macAdded);
+        if (elan == null) {
+            logger.warn("Could not find ELAN for mac {} being added", macAddress);
+            return;
+        }
+
+        String elanName = elan.getElanInstanceName();
+        L2GatewayDevice elanL2GwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName, hwvtepNodeId);
+        if (elanL2GwDevice == null) {
+            logger.warn("Could not find L2GatewayDevice for ELAN: {}, nodeID:{} from cache", elanName, hwvtepNodeId);
+            return;
+        }
+
+        // Cache MAC for furthur processing later
+        elanL2GwDevice.addUcastLocalMac(macAdded);
+
+        ElanL2GatewayUtils.installL2GwUcastMacInElan(entityOwnershipService, bindingNormalizedNodeSerializer, elan,
+                elanL2GwDevice, macAddress);
+    }
+
+    @Override
+    protected InstanceIdentifier<LocalUcastMacs> getWildCardPath() {
+        return InstanceIdentifier.create(LocalUcastMacs.class);
+    }
+
+    @Override
+    protected ClusteredDataChangeListener getDataChangeListener() {
+        return HwvtepLocalUcastMacListener.this;
+    }
+
+    @Override
+    protected DataChangeScope getDataChangeScope() {
+        return DataChangeScope.BASE;
+    }
+}
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLogicalSwitchListener.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLogicalSwitchListener.java
new file mode 100644 (file)
index 0000000..9edbd86
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * 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.vpnservice.elan.l2gw.listeners;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
+import org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase;
+import org.opendaylight.vpnservice.datastoreutils.DataStoreJobCoordinator;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayMulticastUtils;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayUtils;
+import org.opendaylight.vpnservice.elan.l2gw.utils.L2GatewayConnectionUtils;
+import org.opendaylight.vpnservice.elan.utils.ElanUtils;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.vpnservice.utils.SystemPropertyReader;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
+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;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * The listener class for listening to {@code LogicalSwitches}
+ * add/delete/update.
+ *
+ * @see LogicalSwitches
+ */
+public class HwvtepLogicalSwitchListener
+        extends AsyncDataChangeListenerBase<LogicalSwitches, HwvtepLogicalSwitchListener> {
+
+    /** The Constant LOG. */
+    private static final Logger LOG = LoggerFactory.getLogger(HwvtepLogicalSwitchListener.class);
+
+    /** The node id. */
+    private NodeId nodeId;
+
+    /** The logical switch name. */
+    private String logicalSwitchName;
+
+    /** The physical device. */
+    private Devices physicalDevice;
+
+    /** The l2 gateway device. */
+    private L2GatewayDevice l2GatewayDevice;
+
+    /** The default vlan id. */
+    private Integer defaultVlanId;
+
+    /**
+     * Instantiates a new hardware vtep logical switch listener.
+     *
+     * @param l2GatewayDevice
+     *            the l2 gateway device
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @param physicalDevice
+     *            the physical device
+     * @param defaultVlanId
+     *            the default vlan id
+     */
+    public HwvtepLogicalSwitchListener(L2GatewayDevice l2GatewayDevice, String logicalSwitchName,
+            Devices physicalDevice, Integer defaultVlanId) {
+        super(LogicalSwitches.class, HwvtepLogicalSwitchListener.class);
+        this.nodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId());
+        this.logicalSwitchName = logicalSwitchName;
+        this.physicalDevice = physicalDevice;
+        this.l2GatewayDevice = l2GatewayDevice;
+        this.defaultVlanId = defaultVlanId;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * getWildCardPath()
+     */
+    @Override
+       public InstanceIdentifier<LogicalSwitches> getWildCardPath() {
+        return HwvtepSouthboundUtils.createLogicalSwitchesInstanceIdentifier(nodeId,
+                new HwvtepNodeName(logicalSwitchName));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * getDataChangeListener()
+     */
+    @Override
+    protected DataChangeListener getDataChangeListener() {
+        return HwvtepLogicalSwitchListener.this;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * getDataChangeScope()
+     */
+    @Override
+    protected AsyncDataBroker.DataChangeScope getDataChangeScope() {
+        return AsyncDataBroker.DataChangeScope.BASE;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * remove(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
+     * org.opendaylight.yangtools.yang.binding.DataObject)
+     */
+    @Override
+    protected void remove(InstanceIdentifier<LogicalSwitches> identifier, LogicalSwitches deletedLogicalSwitch) {
+        LOG.trace("Received Remove DataChange Notification for identifier: {}, LogicalSwitches: {}", identifier,
+                deletedLogicalSwitch);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * update(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
+     * org.opendaylight.yangtools.yang.binding.DataObject,
+     * org.opendaylight.yangtools.yang.binding.DataObject)
+     */
+    @Override
+    protected void update(InstanceIdentifier<LogicalSwitches> identifier, LogicalSwitches logicalSwitchOld,
+            LogicalSwitches logicalSwitchNew) {
+        LOG.trace("Received Update DataChange Notification for identifier: {}, LogicalSwitches old: {}, new: {}."
+                + "No Action Performed.", identifier, logicalSwitchOld, logicalSwitchNew);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * add(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
+     * org.opendaylight.yangtools.yang.binding.DataObject)
+     */
+    @Override
+    protected void add(InstanceIdentifier<LogicalSwitches> identifier, LogicalSwitches logicalSwitchNew) {
+        LOG.debug("Received Add DataChange Notification for identifier: {}, LogicalSwitches: {}", identifier,
+                logicalSwitchNew);
+        try {
+            L2GatewayConnectionUtils.addL2DeviceToElanL2GwCache(logicalSwitchNew.getHwvtepNodeName().getValue(), l2GatewayDevice);
+            DataStoreJobCoordinator jobCoordinator = DataStoreJobCoordinator.getInstance();
+            LogicalSwitchAddedWorker logicalSwitchAddedWorker = new LogicalSwitchAddedWorker(nodeId, logicalSwitchNew);
+            String jobKey = ElanL2GatewayUtils.getL2GatewayConnectionJobKey(nodeId.getValue(),
+                    logicalSwitchNew.getHwvtepNodeName().getValue());
+            jobCoordinator.enqueueJob(jobKey, logicalSwitchAddedWorker,
+                    SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+
+        } catch (Exception e) {
+            LOG.error("Failed to handle HwVTEPLogicalSwitch - add: {}", e);
+        } finally {
+            try {
+                // This listener is specific to handle a specific logical
+                // switch, hence closing it.
+                LOG.trace("Closing LogicalSwitches listener for node: {}, logicalSwitch: {}", nodeId.getValue(),
+                        logicalSwitchName);
+                close();
+            } catch (Exception e) {
+                LOG.warn("Failed to close HwVTEPLogicalSwitchListener: {}", e);
+            }
+        }
+    }
+
+    /**
+     * The Class LogicalSwitchAddedWorker.
+     */
+    private class LogicalSwitchAddedWorker implements Callable<List<ListenableFuture<Void>>> {
+        /** The logical switch new. */
+        LogicalSwitches logicalSwitchNew;
+
+        /**
+         * Instantiates a new logical switch added worker.
+         *
+         * @param nodeId
+         *            the node id
+         * @param logicalSwitchNew
+         *            the logical switch new
+         */
+        public LogicalSwitchAddedWorker(NodeId nodeId, LogicalSwitches logicalSwitchNew) {
+            this.logicalSwitchNew = logicalSwitchNew;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see java.util.concurrent.Callable#call()
+         */
+        @Override
+        public List<ListenableFuture<Void>> call() throws Exception {
+            try {
+                List<ListenableFuture<Void>> futures = new ArrayList<>();
+                String elan = ElanL2GatewayUtils.getElanFromLogicalSwitch(logicalSwitchName);
+                final L2GatewayDevice elanL2GwDevice = ElanL2GwCacheUtils
+                        .getL2GatewayDeviceFromCache(elan, l2GatewayDevice.getHwvtepNodeId());
+                if (elanL2GwDevice == null) {
+                    LOG.error("Could not find L2GatewayDevice for ELAN: {}, nodeID:{} from cache",
+                            l2GatewayDevice.getHwvtepNodeId());
+                    return null;
+                } else {
+                    LOG.trace("got logical switch device {}", elanL2GwDevice);
+                    futures.add(ElanL2GatewayUtils.updateVlanBindingsInL2GatewayDevice(
+                            new NodeId(elanL2GwDevice.getHwvtepNodeId()), logicalSwitchName, physicalDevice, defaultVlanId));
+                    futures.add(ElanL2GatewayMulticastUtils.handleMcastForElanL2GwDeviceAdd(logicalSwitchName, elanL2GwDevice));
+
+                    HwvtepRemoteMcastMacListener list = new HwvtepRemoteMcastMacListener(ElanUtils.getDataBroker(),
+                            logicalSwitchName, elanL2GwDevice,
+                            new Callable<List<ListenableFuture<Void>>>() {
+
+                            @Override
+                            public List<ListenableFuture<Void>> call() {
+                                List<ListenableFuture<Void>> futures = new ArrayList<>();
+                                futures.add(ElanL2GatewayUtils.installElanMacsInL2GatewayDevice(
+                                        logicalSwitchName, elanL2GwDevice));
+                                return futures;
+                            }}
+                        );
+                    return futures;
+                }
+            } catch (Throwable e) {
+                LOG.error("failed to add ls ", e);
+                return null;
+            }
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepNodeListener.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepNodeListener.java
new file mode 100644 (file)
index 0000000..3891246
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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.vpnservice.elan.l2gw.listeners;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
+import org.opendaylight.vpnservice.datastoreutils.AsyncClusteredDataChangeListenerBase;
+import org.opendaylight.vpnservice.elan.internal.ElanInstanceManager;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayUtils;
+import org.opendaylight.vpnservice.elan.l2gw.utils.L2GatewayConnectionUtils;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.L2gatewayConnections;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.TunnelIps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+public class HwvtepNodeListener
+        extends AsyncClusteredDataChangeListenerBase<Node, HwvtepNodeListener> {
+    private static final Logger LOG = LoggerFactory.getLogger(HwvtepNodeListener.class);
+
+    private DataBroker dataBroker;
+    private EntityOwnershipService entityOwnershipService;
+    private BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer;
+    private ItmRpcService itmRpcService;
+    ElanInstanceManager elanInstanceManager;
+
+    public HwvtepNodeListener(final DataBroker dataBroker, EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, ElanInstanceManager elanInstanceManager,
+            ItmRpcService itmRpcService) {
+        super(Node.class, HwvtepNodeListener.class);
+        this.dataBroker = dataBroker;
+        this.entityOwnershipService = entityOwnershipService;
+        this.bindingNormalizedNodeSerializer = bindingNormalizedNodeSerializer;
+        this.itmRpcService = itmRpcService;
+        this.elanInstanceManager = elanInstanceManager;
+    }
+
+    @Override
+    protected InstanceIdentifier<Node> getWildCardPath() {
+        return InstanceIdentifier.create(NetworkTopology.class)
+                .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID)).child(Node.class);
+    }
+
+    @Override
+    protected HwvtepNodeListener getDataChangeListener() {
+        return HwvtepNodeListener.this;
+    }
+
+    @Override
+    protected DataChangeScope getDataChangeScope() {
+        return AsyncDataBroker.DataChangeScope.BASE;
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<Node> key, Node nodeDeleted) {
+        LOG.debug("Received Node Remove Event: {}, {}", key, nodeDeleted.getNodeId().getValue());
+
+        PhysicalSwitchAugmentation psAugmentation = nodeDeleted.getAugmentation(PhysicalSwitchAugmentation.class);
+        if (psAugmentation != null) {
+            String psName = psAugmentation.getHwvtepNodeName().getValue();
+            L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(psName);
+            if (l2GwDevice != null) {
+                if (!L2GatewayConnectionUtils.isGatewayAssociatedToL2Device(l2GwDevice)) {
+                    L2GatewayCacheUtils.removeL2DeviceFromCache(psName);
+                }
+                l2GwDevice.setConnected(false);
+                ElanL2GwCacheUtils.removeL2GatewayDeviceFromAllElanCache(psName);
+            } else {
+                LOG.error("Unable to find L2 Gateway details for {}", psName);
+            }
+        }
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<Node> key, Node nodeBefore, Node nodeAfter) {
+        LOG.debug("Received Node Update Event: {}, {}, {}", key, nodeBefore, nodeAfter);
+    }
+
+    @Override
+    protected void add(InstanceIdentifier<Node> key, Node nodeAdded) {
+        LOG.debug("Received Node Add Event: {}, {}", key, nodeAdded.getNodeId().getValue());
+
+        PhysicalSwitchAugmentation psAugmentation = nodeAdded.getAugmentation(PhysicalSwitchAugmentation.class);
+        if (psAugmentation != null) {
+            String psName = psAugmentation.getHwvtepNodeName().getValue();
+            L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(psName);
+            if (l2GwDevice == null) {
+                l2GwDevice = new L2GatewayDevice();
+                l2GwDevice.setDeviceName(psName);
+                L2GatewayCacheUtils.addL2DeviceToCache(psName, l2GwDevice);
+            }
+
+            String hwvtepNodeId = getManagedByNodeId(psAugmentation.getManagedBy());
+            l2GwDevice.setHwvtepNodeId(hwvtepNodeId);
+            List<TunnelIps> tunnelIps = psAugmentation.getTunnelIps();
+            if (tunnelIps != null) {
+                for (TunnelIps tunnelIp : tunnelIps) {
+                    IpAddress tunnelIpAddr = tunnelIp.getTunnelIpsKey();
+                    l2GwDevice.addTunnelIp(tunnelIpAddr);
+                    if (L2GatewayConnectionUtils.isGatewayAssociatedToL2Device(l2GwDevice)) {
+                        // It's a pre-provision scenario
+                        // Initiate ITM tunnel creation
+                        ElanL2GatewayUtils.createItmTunnels(itmRpcService, hwvtepNodeId, psName, tunnelIpAddr);
+
+                        // Initiate Logical switch creation for associated L2
+                        // Gateway Connections
+                        List<L2gatewayConnection> l2GwConns = getAssociatedL2GwConnections(dataBroker,
+                                l2GwDevice.getL2GatewayIds());
+                        if (l2GwConns != null) {
+                            for (L2gatewayConnection l2GwConn : l2GwConns) {
+                                L2GatewayConnectionUtils.addL2GatewayConnection(dataBroker, entityOwnershipService,
+                                        bindingNormalizedNodeSerializer, elanInstanceManager, l2GwConn, psName);
+                            }
+                        }
+                        //TODO handle deleted l2gw connections while the device is offline
+                    }
+                }
+            }
+        }
+    }
+
+    private List<L2gatewayConnection> getAssociatedL2GwConnections(DataBroker broker, List<Uuid> l2GatewayIds) {
+        List<L2gatewayConnection> l2GwConnections = null;
+        List<L2gatewayConnection> allL2GwConns = getAllL2gatewayConnections(broker);
+        if (allL2GwConns != null) {
+            l2GwConnections = new ArrayList<L2gatewayConnection>();
+            for (Uuid l2GatewayId : l2GatewayIds) {
+                for (L2gatewayConnection l2GwConn : allL2GwConns) {
+                    if (l2GwConn.getL2gatewayId().equals(l2GatewayId)) {
+                        l2GwConnections.add(l2GwConn);
+                    }
+                }
+            }
+        }
+        return l2GwConnections;
+    }
+
+    protected List<L2gatewayConnection> getAllL2gatewayConnections(DataBroker broker) {
+        InstanceIdentifier<L2gatewayConnections> inst = InstanceIdentifier.create(Neutron.class)
+                .child(L2gatewayConnections.class);
+        Optional<L2gatewayConnections> l2GwConns = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst);
+        if (l2GwConns.isPresent()) {
+            return l2GwConns.get().getL2gatewayConnection();
+        }
+        return null;
+    }
+
+    private String getManagedByNodeId(HwvtepGlobalRef globalRef) {
+        InstanceIdentifier<?> instId = globalRef.getValue();
+        return instId.firstKeyOf(Node.class, NodeKey.class).getNodeId().getValue();
+    }
+}
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepRemoteMcastMacListener.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepRemoteMcastMacListener.java
new file mode 100644 (file)
index 0000000..5204627
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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.vpnservice.elan.l2gw.listeners;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase;
+import org.opendaylight.vpnservice.datastoreutils.DataStoreJobCoordinator;
+import org.opendaylight.vpnservice.elan.l2gw.utils.ElanL2GatewayUtils;
+import org.opendaylight.vpnservice.elan.utils.ElanConstants;
+import org.opendaylight.vpnservice.elan.utils.ElanUtils;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.vpnservice.utils.SystemPropertyReader;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * The listener class for listening to {@code RemoteMcastMacs}
+ * add/delete/update.
+ *
+ * @see RemoteMcastMacs
+ */
+public class HwvtepRemoteMcastMacListener
+        extends AsyncDataChangeListenerBase<RemoteMcastMacs, HwvtepRemoteMcastMacListener> {
+
+    /** The Constant LOG. */
+    private static final Logger LOG = LoggerFactory.getLogger(HwvtepRemoteMcastMacListener.class);
+
+    /** The node id. */
+    private NodeId nodeId;
+
+    DataBroker broker;
+
+    String logicalSwitchName;
+
+    AtomicBoolean executeTask = new AtomicBoolean(true);
+
+    Callable<List<ListenableFuture<Void>>> taskToRun;
+    /**
+     * Instantiates a new remote mcast mac listener.
+     *
+     * @param broker
+     *            the mdsal databroker reference
+     * @param logicalSwitchName
+     * @param l2GatewayDevice
+     *            the l2 gateway device
+     * @param task
+     *            the task to be run upon data presence
+     * @throws Exception
+     */
+    public HwvtepRemoteMcastMacListener(DataBroker broker, String logicalSwitchName, L2GatewayDevice l2GatewayDevice,
+            Callable<List<ListenableFuture<Void>>> task) throws Exception {
+        super(RemoteMcastMacs.class, HwvtepRemoteMcastMacListener.class);
+        this.nodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId());
+        this.broker = broker;
+        this.taskToRun = task;
+        this.logicalSwitchName = logicalSwitchName;
+        LOG.debug("registering the listener for mcast mac ");
+        registerListener(LogicalDatastoreType.OPERATIONAL, broker);
+        if (isDataPresentInOpDs(getWildCardPath())) {
+            LOG.debug("mcast mac already present running the task ");
+            if (executeTask.compareAndSet(true, false)) {
+                runTask();
+            }
+        }
+    }
+
+    private boolean isDataPresentInOpDs(InstanceIdentifier<? extends DataObject> path) throws Exception {
+        Optional<? extends DataObject> mac = null;
+        try {
+            mac = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, path);
+        } catch (Throwable e) {
+        }
+        if (mac == null || !mac.isPresent()) {
+            return false;
+        }
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * getWildCardPath()
+     */
+    @Override
+    public InstanceIdentifier<RemoteMcastMacs> getWildCardPath() {
+        return HwvtepSouthboundUtils.createRemoteMcastMacsInstanceIdentifier(nodeId,
+                logicalSwitchName, new MacAddress(ElanConstants.UNKNOWN_DMAC));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * getDataChangeListener()
+     */
+    @Override
+    protected DataChangeListener getDataChangeListener() {
+        return HwvtepRemoteMcastMacListener.this;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * getDataChangeScope()
+     */
+    @Override
+    protected AsyncDataBroker.DataChangeScope getDataChangeScope() {
+        return AsyncDataBroker.DataChangeScope.BASE;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * remove(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
+     * org.opendaylight.yangtools.yang.binding.DataObject)
+     */
+    @Override
+    protected void remove(InstanceIdentifier<RemoteMcastMacs> identifier, RemoteMcastMacs deleted) {
+        LOG.trace("Received Remove DataChange Notification for identifier: {}, RemoteMcastMacs: {}", identifier,
+                deleted);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * update(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
+     * org.opendaylight.yangtools.yang.binding.DataObject,
+     * org.opendaylight.yangtools.yang.binding.DataObject)
+     */
+    @Override
+    protected void update(InstanceIdentifier<RemoteMcastMacs> identifier, RemoteMcastMacs old,
+            RemoteMcastMacs newdata) {
+        LOG.trace("Received Update DataChange Notification for identifier: {}, RemoteMcastMacs old: {}, new: {}."
+                + "No Action Performed.", identifier, old, newdata);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.opendaylight.vpnservice.datastoreutils.AsyncDataChangeListenerBase#
+     * add(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
+     * org.opendaylight.yangtools.yang.binding.DataObject)
+     */
+    @Override
+    protected void add(InstanceIdentifier<RemoteMcastMacs> identifier, RemoteMcastMacs mcastMac) {
+        LOG.debug("Received Add DataChange Notification for identifier: {}, RemoteMcastMacs: {}", identifier,
+                mcastMac);
+        if (executeTask.compareAndSet(true, false)) {
+            runTask();
+        }
+    }
+
+    void runTask() {
+        try {
+            DataStoreJobCoordinator jobCoordinator = DataStoreJobCoordinator.getInstance();
+            String jobKey = ElanL2GatewayUtils.getL2GatewayConnectionJobKey(nodeId.getValue(), ElanConstants.UNKNOWN_DMAC);
+            jobCoordinator.enqueueJob(jobKey, taskToRun, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+        } catch (Exception e) {
+            LOG.error("Failed to handle remote mcast mac - add: {}", e);
+        } finally {
+            try {
+                close();
+            } catch (Exception e) {
+                LOG.warn("Failed to close McastMacSwitchListener: {}", e);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/L2GatewayConnectionListener.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/L2GatewayConnectionListener.java
new file mode 100644 (file)
index 0000000..305bb1c
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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.vpnservice.elan.l2gw.listeners;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener;
+import org.opendaylight.vpnservice.datastoreutils.AsyncClusteredDataChangeListenerBase;
+import org.opendaylight.vpnservice.elan.internal.ElanInstanceManager;
+import org.opendaylight.vpnservice.elan.l2gw.utils.L2GatewayConnectionUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.L2gatewayConnections;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class L2GatewayConnectionListener extends AsyncClusteredDataChangeListenerBase<L2gatewayConnection,
+        L2GatewayConnectionListener> implements AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(L2GatewayConnectionListener.class);
+
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private final DataBroker broker;
+    private EntityOwnershipService entityOwnershipService;
+    private BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer;
+    private ElanInstanceManager elanInstanceManager;
+
+    public L2GatewayConnectionListener(final DataBroker db, EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, ElanInstanceManager elanInstanceManager) {
+        super(L2gatewayConnection.class, L2GatewayConnectionListener.class);
+        broker = db;
+        this.entityOwnershipService = entityOwnershipService;
+        this.bindingNormalizedNodeSerializer = bindingNormalizedNodeSerializer;
+        this.elanInstanceManager = elanInstanceManager;
+        registerListener(db);
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            try {
+                listenerRegistration.close();
+            } catch (final Exception e) {
+                LOG.error("Error when cleaning up DataChangeListener.", e);
+            }
+            listenerRegistration = null;
+        }
+        LOG.info("L2 Gateway Connection listener Closed");
+    }
+
+    private void registerListener(final DataBroker db) {
+        try {
+            listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+                    InstanceIdentifier.create(Neutron.class).child(L2gatewayConnections.class)
+                            .child(L2gatewayConnection.class),
+                    L2GatewayConnectionListener.this, DataChangeScope.SUBTREE);
+        } catch (final Exception e) {
+            LOG.error("Neutron Manager L2 Gateway Connection DataChange listener registration fail!", e);
+            throw new IllegalStateException(
+                    "Neutron Manager L2 Gateway Connection DataChange listener registration failed.", e);
+        }
+    }
+
+    @Override
+    protected void add(final InstanceIdentifier<L2gatewayConnection> identifier, final L2gatewayConnection input) {
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("Adding L2gatewayConnection : key: " + identifier + ", value=" + input);
+        }
+
+        // Get associated L2GwId from 'input'
+        // Create logical switch in each of the L2GwDevices part of L2Gw
+        // Logical switch name is network UUID
+        // Add L2GwDevices to ELAN
+        L2GatewayConnectionUtils.addL2GatewayConnection(broker, entityOwnershipService, bindingNormalizedNodeSerializer,
+                elanInstanceManager, input);
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<L2gatewayConnection> identifier, L2gatewayConnection input) {
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("Removing L2gatewayConnection : key: " + identifier + ", value=" + input);
+        }
+
+        L2GatewayConnectionUtils.deleteL2GatewayConnection(broker, entityOwnershipService, bindingNormalizedNodeSerializer,
+                elanInstanceManager, input);
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<L2gatewayConnection> identifier, L2gatewayConnection original,
+            L2gatewayConnection update) {
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("Updating L2gatewayConnection : key: " + identifier + ", original value=" + original
+                    + ", update value=" + update);
+        }
+    }
+
+    @Override
+    protected InstanceIdentifier<L2gatewayConnection> getWildCardPath() {
+        return InstanceIdentifier.create(L2gatewayConnection.class);
+    }
+
+    @Override
+    protected ClusteredDataChangeListener getDataChangeListener() {
+        return L2GatewayConnectionListener.this;
+    }
+
+    @Override
+    protected DataChangeScope getDataChangeScope() {
+        return DataChangeScope.BASE;
+    }
+}
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayMulticastUtils.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayMulticastUtils.java
new file mode 100644 (file)
index 0000000..498530c
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * 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.vpnservice.elan.l2gw.utils;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+
+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.elanmanager.utils.ElanL2GwCacheUtils;
+import org.opendaylight.vpnservice.elan.internal.ElanInstanceManager;
+import org.opendaylight.vpnservice.elan.internal.ElanInterfaceManager;
+import org.opendaylight.vpnservice.elan.utils.ElanConstants;
+import org.opendaylight.vpnservice.elan.utils.ElanUtils;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundUtils;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepLogicalSwitchRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSetBuilder;
+//import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev150129.DesignatedSwitchesForExternalTunnels;
+//import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev150129.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel;
+//import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev150129.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.instances.ElanInstance;
+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;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * The utility class to handle ELAN L2 Gateway related to multicast.
+ */
+public class ElanL2GatewayMulticastUtils {
+
+    /** The Constant LOG. */
+    private static final Logger LOG = LoggerFactory.getLogger(ElanL2GatewayMulticastUtils.class);
+
+    /** The broker. */
+    private static DataBroker broker;
+
+    /** The elan instance manager. */
+    private static ElanInstanceManager elanInstanceManager;
+
+    /** The elan interface manager. */
+    private static ElanInterfaceManager elanInterfaceManager;
+
+    /**
+     * Sets the broker.
+     *
+     * @param broker
+     *            the new broker
+     */
+    public static void setBroker(DataBroker broker) {
+        ElanL2GatewayMulticastUtils.broker = broker;
+    }
+
+    /**
+     * Sets the elan instance manager.
+     *
+     * @param elanMgr
+     *            the new elan instance manager
+     */
+    public static void setElanInstanceManager(ElanInstanceManager elanMgr) {
+        ElanL2GatewayMulticastUtils.elanInstanceManager = elanMgr;
+    }
+
+    /**
+     * Sets the elan interface manager.
+     *
+     * @param interfaceMgr
+     *            the new elan interface manager
+     */
+    public static void setElanInterfaceManager(ElanInterfaceManager interfaceMgr) {
+        elanInterfaceManager = interfaceMgr;
+    }
+
+    /**
+     * Handle mcast for elan l2 gw device add.
+     *
+     * @param elanName
+     *            the elan name
+     * @param device
+     *            the device
+     * @return the listenable future
+     */
+    public static ListenableFuture<Void> handleMcastForElanL2GwDeviceAdd(String elanName, L2GatewayDevice device) {
+        return updateMcastMacs(elanName, device, true/* updateThisDevice */);
+    }
+
+    /**
+     * Updates the remote mcast mac table for all the devices in this elan
+     * includes all the dpn tep ips and other devices tep ips in broadcast
+     * locator set.
+     *
+     * @param elanName
+     *            the elan to be updated
+     * @return the listenable future
+     */
+    public static ListenableFuture<Void> updateRemoteMcastMacOnElanL2GwDevices(String elanName) {
+        SettableFuture<Void> future = SettableFuture.create();
+        future.set(null);
+        try {
+            ConcurrentMap<String, L2GatewayDevice> mapL2gwDevices = ElanL2GwCacheUtils
+                    .getAllElanL2GatewayDevicesFromCache(elanName);
+            if (mapL2gwDevices == null || mapL2gwDevices.isEmpty()) {
+                LOG.trace("No L2GatewayDevices to configure RemoteMcastMac for elan {}", elanName);
+                return future;
+            }
+            List<DpnInterfaces> dpns = ElanUtils.getInvolvedDpnsInElan(elanName);
+
+            // TODO revisit
+            L2GatewayDevice firstDevice = mapL2gwDevices.values().iterator().next();
+            List<IpAddress> dpnsTepIps = getAllTepIpsOfDpns(firstDevice, dpns);
+            List<IpAddress> l2GwDevicesTepIps = getAllTepIpsOfL2GwDevices(mapL2gwDevices);
+
+            WriteTransaction transaction = broker.newWriteOnlyTransaction();
+            for (L2GatewayDevice device : mapL2gwDevices.values()) {
+                updateRemoteMcastMac(transaction, elanName, device, dpnsTepIps, l2GwDevicesTepIps);
+            }
+            return transaction.submit();
+        } catch (Throwable e) {
+            LOG.error("Failed to configure mcast mac on elan " + elanName, e);
+        }
+        return future;
+    }
+
+    /**
+     * Update mcast macs.
+     *
+     * @param elanName
+     *            the elan name
+     * @param device
+     *            the device
+     * @param updateThisDevice
+     *            the update this device
+     * @return the listenable future
+     */
+    public static ListenableFuture<Void> updateMcastMacs(String elanName, L2GatewayDevice device,
+            boolean updateThisDevice) {
+
+        SettableFuture<Void> ft = SettableFuture.create();
+        ft.set(null);
+
+        ElanInstance elanInstance = elanInstanceManager.getElanInstanceByName(elanName);
+        elanInterfaceManager.updateElanBroadcastGroup(elanInstance);
+
+        List<DpnInterfaces> dpns = ElanUtils.getInvolvedDpnsInElan(elanName);
+
+        ConcurrentMap<String, L2GatewayDevice> devices = ElanL2GwCacheUtils
+                .getAllElanL2GatewayDevicesFromCache(elanName);
+
+        List<IpAddress> dpnsTepIps = getAllTepIpsOfDpns(device, dpns);
+        List<IpAddress> l2GwDevicesTepIps = getAllTepIpsOfL2GwDevices(devices);
+        // if (allTepIps.size() < 2) {
+        // LOG.debug("no other devices are found in the elan {}", elanName);
+        // return ft;
+        // }
+
+        WriteTransaction transaction = broker.newWriteOnlyTransaction();
+        if (updateThisDevice) {
+            updateRemoteMcastMac(transaction, elanName, device, dpnsTepIps, l2GwDevicesTepIps);
+        }
+
+        // TODO: Need to revisit below logic as logical switches might not be
+        // created to configure RemoteMcastMac entry
+        for (L2GatewayDevice otherDevice : devices.values()) {
+            if (!otherDevice.getDeviceName().equals(device.getDeviceName())) {
+                updateRemoteMcastMac(transaction, elanName, otherDevice, dpnsTepIps, l2GwDevicesTepIps);
+            }
+        }
+        return transaction.submit();
+
+    }
+
+    /**
+     * Update remote mcast mac.
+     *
+     * @param transaction
+     *            the transaction
+     * @param elanName
+     *            the elan name
+     * @param device
+     *            the device
+     * @param dpnsTepIps
+     *            the dpns tep ips
+     * @param l2GwDevicesTepIps
+     *            the l2 gw devices tep ips
+     * @return the write transaction
+     */
+    private static WriteTransaction updateRemoteMcastMac(WriteTransaction transaction, String elanName,
+            L2GatewayDevice device, List<IpAddress> dpnsTepIps, List<IpAddress> l2GwDevicesTepIps) {
+        NodeId nodeId = new NodeId(device.getHwvtepNodeId());
+        String logicalSwitchName = ElanL2GatewayUtils.getLogicalSwitchFromElan(elanName);
+
+        ArrayList<IpAddress> otherTepIps = new ArrayList<>(l2GwDevicesTepIps);
+        otherTepIps.remove(device.getTunnelIp());
+
+        if (!dpnsTepIps.isEmpty()) {
+            otherTepIps.addAll(dpnsTepIps);
+        } else {
+            // If no dpns in elan, configure dhcp designated switch Tep Ip as a
+            // physical locator in l2 gw device
+            IpAddress dhcpDesignatedSwitchTepIp = getTepIpOfDesignatedSwitchForExternalTunnel(device, elanName);
+            if (dhcpDesignatedSwitchTepIp != null) {
+                otherTepIps.add(dhcpDesignatedSwitchTepIp);
+
+                HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
+                        .createHwvtepPhysicalLocatorAugmentation(String.valueOf(dhcpDesignatedSwitchTepIp.getValue()));
+                HwvtepUtils.putPhysicalLocator(transaction, nodeId, phyLocatorAug);
+
+                LOG.info(
+                        "Adding PhysicalLocator for node: {} with Dhcp designated switch Tep Ip {} as physical locator, elan {}",
+                        device.getHwvtepNodeId(), String.valueOf(dhcpDesignatedSwitchTepIp.getValue()), elanName);
+            } else {
+                LOG.warn("Dhcp designated switch Tep Ip not found for l2 gw node {} and elan {}",
+                        device.getHwvtepNodeId(), elanName);
+            }
+        }
+
+        putRemoteMcastMac(transaction, nodeId, logicalSwitchName, otherTepIps);
+        LOG.info("Adding RemoteMcastMac for node: {} with physical locators: {}", device.getHwvtepNodeId(),
+                otherTepIps);
+        return transaction;
+    }
+
+    /**
+     * Put remote mcast mac in config DS.
+     *
+     * @param transaction
+     *            the transaction
+     * @param nodeId
+     *            the node id
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @param tepIps
+     *            the tep ips
+     */
+    private static void putRemoteMcastMac(WriteTransaction transaction, NodeId nodeId, String logicalSwitchName,
+            ArrayList<IpAddress> tepIps) {
+        List<LocatorSet> locators = new ArrayList<>();
+        for (IpAddress tepIp : tepIps) {
+            HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
+                    .createHwvtepPhysicalLocatorAugmentation(String.valueOf(tepIp.getValue()));
+            HwvtepPhysicalLocatorRef phyLocRef = new HwvtepPhysicalLocatorRef(
+                    HwvtepSouthboundUtils.createPhysicalLocatorInstanceIdentifier(nodeId, phyLocatorAug));
+            locators.add(new LocatorSetBuilder().setLocatorRef(phyLocRef).build());
+        }
+
+        HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils
+                .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName)));
+        RemoteMcastMacs remoteUcastMac = new RemoteMcastMacsBuilder()
+                .setMacEntryKey(new MacAddress(ElanConstants.UNKNOWN_DMAC)).setLogicalSwitchRef(lsRef)
+                .setLocatorSet(locators).build();
+        HwvtepUtils.putRemoteMcastMac(transaction, nodeId, remoteUcastMac);
+    }
+
+    /**
+     * Gets all the tep ips of dpns.
+     *
+     * @param device
+     *            the device
+     * @param dpns
+     *            the dpns
+     * @param devices
+     *            the devices
+     * @return the all tep ips of dpns and devices
+     */
+    private static List<IpAddress> getAllTepIpsOfDpns(L2GatewayDevice l2GwDevice, List<DpnInterfaces> dpns) {
+        List<IpAddress> tepIps = new ArrayList<>();
+        for (DpnInterfaces dpn : dpns) {
+            IpAddress internalTunnelIp = ElanL2GatewayUtils.getSourceDpnTepIp(dpn.getDpId(),
+                    new NodeId(l2GwDevice.getHwvtepNodeId()));
+            if (internalTunnelIp != null) {
+                tepIps.add(internalTunnelIp);
+            }
+        }
+        return tepIps;
+    }
+
+    /**
+     * Gets the all tep ips of l2 gw devices.
+     *
+     * @param devices
+     *            the devices
+     * @return the all tep ips of l2 gw devices
+     */
+    private static List<IpAddress> getAllTepIpsOfL2GwDevices(ConcurrentMap<String, L2GatewayDevice> devices) {
+        List<IpAddress> tepIps = new ArrayList<>();
+        for (L2GatewayDevice otherDevice : devices.values()) {
+            tepIps.add(otherDevice.getTunnelIp());
+        }
+        return tepIps;
+    }
+
+    /**
+     * Handle mcast for elan l2 gw device delete.
+     *
+     * @param elanInstance
+     *            the elan instance
+     * @param l2GatewayDevice
+     *            the l2 gateway device
+     * @return the listenable future
+     */
+    public static List<ListenableFuture<Void>> handleMcastForElanL2GwDeviceDelete(ElanInstance elanInstance,
+            L2GatewayDevice l2GatewayDevice) {
+        ListenableFuture<Void> updateMcastMacsFuture = updateMcastMacs(elanInstance.getElanInstanceName(),
+                l2GatewayDevice, false/* updateThisDevice */);
+        ListenableFuture<Void> deleteRemoteMcastMacFuture = deleteRemoteMcastMac(
+                new NodeId(l2GatewayDevice.getHwvtepNodeId()), elanInstance.getElanInstanceName());
+        return Lists.newArrayList(updateMcastMacsFuture, deleteRemoteMcastMacFuture);
+    }
+
+    /**
+     * Delete remote mcast mac from Hwvtep node.
+     *
+     * @param nodeId
+     *            the node id
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @return the listenable future
+     */
+    private static ListenableFuture<Void> deleteRemoteMcastMac(NodeId nodeId, String logicalSwitchName) {
+        InstanceIdentifier<LogicalSwitches> logicalSwitch = HwvtepSouthboundUtils
+                .createLogicalSwitchesInstanceIdentifier(nodeId, new HwvtepNodeName(logicalSwitchName));
+        RemoteMcastMacsKey remoteMcastMacsKey = new RemoteMcastMacsKey(new HwvtepLogicalSwitchRef(logicalSwitch),
+                new MacAddress(ElanConstants.UNKNOWN_DMAC));
+
+        RemoteMcastMacs remoteMcast = HwvtepUtils.getRemoteMcastMac(broker, LogicalDatastoreType.OPERATIONAL, nodeId,
+                remoteMcastMacsKey);
+        if (remoteMcast != null) {
+            LOG.info("Deleting RemoteMcastMacs entry on node: {} for logical switch: {}", nodeId.getValue(),
+                    logicalSwitchName);
+            return HwvtepUtils.deleteRemoteMcastMac(broker, nodeId, remoteMcastMacsKey);
+        }
+
+        SettableFuture<Void> future = SettableFuture.create();
+        future.set(null);
+        return future;
+    }
+
+    /**
+     * Gets the tep ip of designated switch for external tunnel.
+     *
+     * @param l2GwDevice
+     *            the l2 gw device
+     * @param elanInstanceName
+     *            the elan instance name
+     * @return the tep ip of designated switch for external tunnel
+     */
+    public static IpAddress getTepIpOfDesignatedSwitchForExternalTunnel(L2GatewayDevice l2GwDevice,
+            String elanInstanceName) {
+        IpAddress tepIp = null;
+     // TODO: Uncomment after DHCP changes are merged
+/*        DesignatedSwitchForTunnel desgSwitch = getDesignatedSwitchForExternalTunnel(l2GwDevice.getTunnelIp(),
+                elanInstanceName);
+        if (desgSwitch != null) {
+            tepIp = ElanL2GatewayUtils.getSourceDpnTepIp(BigInteger.valueOf(desgSwitch.getDpId()),
+                    new NodeId(l2GwDevice.getHwvtepNodeId()));
+        }*/
+        return tepIp;
+    }
+
+    /**
+     * Gets the designated switch for external tunnel.
+     *
+     * @param tunnelIp
+     *            the tunnel ip
+     * @param elanInstanceName
+     *            the elan instance name
+     * @return the designated switch for external tunnel
+     */
+    // TODO: Uncomment after DHCP changes are merged
+/*    public static DesignatedSwitchForTunnel getDesignatedSwitchForExternalTunnel(IpAddress tunnelIp,
+            String elanInstanceName) {
+        InstanceIdentifier<DesignatedSwitchForTunnel> instanceIdentifier = InstanceIdentifier
+                .builder(DesignatedSwitchesForExternalTunnels.class)
+                .child(DesignatedSwitchForTunnel.class, new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp))
+                .build();
+        Optional<DesignatedSwitchForTunnel> designatedSwitchForTunnelOptional = MDSALUtil.read(broker,
+                LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
+        if (designatedSwitchForTunnelOptional.isPresent()) {
+            return designatedSwitchForTunnelOptional.get();
+        }
+        return null;
+    }*/
+
+}
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayUtils.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayUtils.java
new file mode 100644 (file)
index 0000000..e695055
--- /dev/null
@@ -0,0 +1,1012 @@
+/*
+ * 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.vpnservice.elan.l2gw.utils;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+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.clustering.EntityOwnershipService;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
+import org.opendaylight.vpnservice.datastoreutils.DataStoreJobCoordinator;
+import org.opendaylight.vpnservice.elan.utils.ElanUtils;
+import org.opendaylight.vpnservice.interfacemgr.IfmUtil;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.vpnservice.utils.SystemPropertyReader;
+import org.opendaylight.vpnservice.utils.clustering.ClusteringUtils;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundConstants;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundUtils;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
+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.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LocalUcastMacs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteUcastMacs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.port.attributes.VlanBindings;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.forwarding.tables.MacTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.instances.ElanInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.forwarding.entries.MacEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.IfTunnel;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.AddL2GwDeviceInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * It gathers a set of utility methods that handle ELAN configuration in external Devices (where external means
+ * "not-CSS". As of now: TORs).
+ *
+ * It makes use of HwvtepUtils class located under ovsdb/hwvtepsouthbound project for low-level mdsal operations
+ *
+ * @author eperefr
+ *
+ */
+public class ElanL2GatewayUtils {
+
+    private static DataBroker         broker;
+    private static ItmRpcService      itmRpcService;
+
+    private static final Logger LOG = LoggerFactory.getLogger(ElanL2GatewayUtils.class);
+
+    /**
+     * Sets the data broker.
+     *
+     * @param dataBroker
+     *            the new data broker
+     */
+    public static void setDataBroker(DataBroker dataBroker) {
+        broker = dataBroker;
+    }
+
+    /**
+     * Sets the itm rpc service.
+     *
+     * @param itmRpc
+     *            the new itm rpc service
+     */
+    public static void setItmRpcService(ItmRpcService itmRpc) {
+        itmRpcService = itmRpc;
+    }
+
+    /**
+     * Installs the given MAC as a remote mac in all external devices (as of
+     * now, TORs) that participate in the given Elan.
+     *
+     * @param elanInstance
+     *            Elan to which the interface belongs to
+     * @param dpId
+     *            Id of the DPN where the macs are located. Needed for selecting
+     *            the right tunnel
+     * @param macAddresses
+     *            the mac addresses
+     */
+    public static void installMacsInElanExternalDevices(ElanInstance elanInstance, BigInteger dpId,
+            List<PhysAddress> macAddresses) {
+        String logicalSwitchName = getElanFromLogicalSwitch(elanInstance.getElanInstanceName());
+        ConcurrentMap<String, L2GatewayDevice> elanDevices = ElanL2GwCacheUtils
+                .getAllElanL2GatewayDevicesFromCache(elanInstance.getElanInstanceName());
+        for (L2GatewayDevice externalDevice : elanDevices.values()) {
+            NodeId nodeId = new NodeId(externalDevice.getHwvtepNodeId());
+            IpAddress dpnTepIp = getSourceDpnTepIp(dpId, nodeId);
+            LOG.trace("Dpn Tep IP: {} for dpnId: {} and nodeId: {}", dpnTepIp, dpId, nodeId);
+            if (dpnTepIp == null) {
+                LOG.error("TEP IP not found for dpnId {} and nodeId {}", dpId, nodeId);
+                continue;
+            }
+            installMacsInExternalDeviceAsRemoteUcastMacs(externalDevice.getHwvtepNodeId(), macAddresses,
+                    logicalSwitchName, dpnTepIp);
+        }
+    }
+
+    /**
+     * Installs a list of Mac Addresses as remote Ucast address in an external
+     * device using the hwvtep-southbound.
+     *
+     * @param deviceNodeId
+     *            NodeId if the ExternalDevice where the macs must be installed
+     *            in.
+     * @param macAddresses
+     *            List of Mac addresses to be installed in the external device.
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @param remoteVtepIp
+     *            VTEP's IP in this CSS used for the tunnel with external
+     *            device.
+     */
+    private static ListenableFuture<Void> installMacsInExternalDeviceAsRemoteUcastMacs(String deviceNodeId,
+            List<PhysAddress> macAddresses, String logicalSwitchName, IpAddress remoteVtepIp) {
+        NodeId nodeId = new NodeId(deviceNodeId);
+        HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
+                .createHwvtepPhysicalLocatorAugmentation(String.valueOf(remoteVtepIp.getValue()));
+        List<RemoteUcastMacs> macs = new ArrayList<RemoteUcastMacs>();
+        for (PhysAddress mac : macAddresses) {
+            // TODO: Query ARP cache to get IP address corresponding to
+            // the MAC
+            IpAddress ipAddress = null;
+            macs.add(HwvtepSouthboundUtils.createRemoteUcastMac(nodeId, mac.getValue(), ipAddress, logicalSwitchName,
+                    phyLocatorAug));
+        }
+        return HwvtepUtils.addRemoteUcastMacs(broker, nodeId, macs);
+    }
+
+    /**
+     * Install macs in external device as remote ucast macs.
+     *
+     * @param elanName
+     *            the elan name
+     * @param lstElanInterfaceNames
+     *            the lst Elan interface names
+     * @param dpnId
+     *            the dpn id
+     * @param externalNodeId
+     *            the external node id
+     * @return the listenable future
+     */
+    public static ListenableFuture<Void> installMacsInExternalDeviceAsRemoteUcastMacs(String elanName,
+            Set<String> lstElanInterfaceNames, BigInteger dpnId, NodeId externalNodeId) {
+        SettableFuture<Void> future = SettableFuture.create();
+        future.set(null);
+        if (lstElanInterfaceNames == null || lstElanInterfaceNames.isEmpty()) {
+            return future;
+        }
+
+        IpAddress dpnTepIp = getSourceDpnTepIp(dpnId, externalNodeId);
+        if (dpnTepIp == null) {
+            return future;
+        }
+
+        WriteTransaction transaction = broker.newWriteOnlyTransaction();
+        HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepUtils.getPhysicalLocator(broker,
+                LogicalDatastoreType.CONFIGURATION, externalNodeId, dpnTepIp);
+        if (phyLocatorAug == null) {
+            phyLocatorAug = HwvtepSouthboundUtils
+                    .createHwvtepPhysicalLocatorAugmentation(String.valueOf(dpnTepIp.getValue()));
+            HwvtepUtils.putPhysicalLocator(transaction, externalNodeId, phyLocatorAug);
+        }
+
+        String logicalSwitchName = getLogicalSwitchFromElan(elanName);
+        for (String interfaceName : lstElanInterfaceNames) {
+            ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(interfaceName);
+            if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) {
+                for (MacEntry macEntry : elanInterfaceMac.getMacEntry()) {
+                    // TODO: Query ARP cache to get IP address corresponding to
+                    // the MAC
+                    IpAddress ipAddress = null;
+                    RemoteUcastMacs mac = HwvtepSouthboundUtils.createRemoteUcastMac(externalNodeId,
+                            macEntry.getMacAddress().getValue(), ipAddress, logicalSwitchName, phyLocatorAug);
+                    HwvtepUtils.putRemoteUcastMac(transaction, externalNodeId, mac);
+                }
+            }
+        }
+        LOG.debug("Installing macs in external device [{}] for dpn [{}], elan [{}], no of interfaces [{}]",
+                externalNodeId.getValue(), dpnId, elanName, lstElanInterfaceNames.size());
+        return transaction.submit();
+    }
+
+    /**
+     * Removes the given MAC Addresses from all the External Devices belonging
+     * to the specified ELAN.
+     *
+     * @param elanInstance
+     *            the elan instance
+     * @param macAddresses
+     *            the mac addresses
+     */
+    public static void removeMacsFromElanExternalDevices(ElanInstance elanInstance, List<PhysAddress> macAddresses) {
+        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices = ElanL2GwCacheUtils
+                .getAllElanL2GatewayDevicesFromCache(elanInstance.getElanInstanceName());
+        for (L2GatewayDevice l2GatewayDevice : elanL2GwDevices.values()) {
+            removeRemoteUcastMacsFromExternalDevice(l2GatewayDevice.getHwvtepNodeId(),
+                    elanInstance.getElanInstanceName(), macAddresses);
+        }
+    }
+
+    /**
+     * Removes the given MAC Addresses from the specified External Device.
+     *
+     * @param deviceNodeId
+     *            the device node id
+     * @param logicalSwitchName
+     * @param macAddresses
+     *            the mac addresses
+     * @return the listenable future
+     */
+    private static ListenableFuture<Void> removeRemoteUcastMacsFromExternalDevice(String deviceNodeId,
+            String logicalSwitchName, List<PhysAddress> macAddresses) {
+        NodeId nodeId = new NodeId(deviceNodeId);
+
+        // TODO (eperefr)
+        List<MacAddress> lstMac = Lists.transform(macAddresses, new Function<PhysAddress, MacAddress>() {
+            @Override
+            public MacAddress apply(PhysAddress physAddress) {
+                return (physAddress != null) ? new MacAddress(physAddress.getValue()) : null;
+            }
+        });
+        return HwvtepUtils.deleteRemoteUcastMacs(broker, nodeId, logicalSwitchName, lstMac);
+    }
+
+    public static ElanInstance getElanInstanceForUcastLocalMac(LocalUcastMacs localUcastMac) {
+        Optional<LogicalSwitches> lsOpc = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL,
+                (InstanceIdentifier<LogicalSwitches>) localUcastMac.getLogicalSwitchRef().getValue());
+        if (lsOpc.isPresent()) {
+            LogicalSwitches ls = lsOpc.get();
+            if (ls != null) {
+                // Logical switch name is Elan name
+                String elanName = getElanFromLogicalSwitch(ls.getHwvtepNodeName().getValue());
+                return ElanUtils.getElanInstanceByName(elanName);
+            } else {
+                String macAddress = localUcastMac.getMacEntryKey().getValue();
+                LOG.error("Could not find logical_switch for {} being added/deleted", macAddress);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Install external device local macs in dpn.
+     *
+     * @param dpnId
+     *            the dpn id
+     * @param l2gwDeviceNodeId
+     *            the l2gw device node id
+     * @param elan
+     *            the elan
+     */
+    public static void installL2gwDeviceLocalMacsInDpn(BigInteger dpnId, NodeId l2gwDeviceNodeId, ElanInstance elan) {
+        String elanName = elan.getElanInstanceName();
+        L2GatewayDevice l2gwDevice = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanName,
+                l2gwDeviceNodeId.getValue());
+        if (l2gwDevice == null) {
+            LOG.debug("L2 gw device not found in elan cache for device name {}", l2gwDeviceNodeId.getValue());
+            return;
+        }
+
+        List<LocalUcastMacs> l2gwDeviceLocalMacs = l2gwDevice.getUcastLocalMacs();
+        if (l2gwDeviceLocalMacs != null && !l2gwDeviceLocalMacs.isEmpty()) {
+            for (LocalUcastMacs localUcastMac : l2gwDeviceLocalMacs) {
+                ElanUtils.installDmacFlowsToExternalRemoteMac(dpnId, l2gwDeviceNodeId.getValue(), elan.getElanTag(),
+                        elan.getVni(), localUcastMac.getMacEntryKey().getValue(), elanName);
+            }
+        }
+        LOG.debug("Installing L2gw device [{}] local macs [size: {}] in dpn [{}] for elan [{}]",
+                l2gwDeviceNodeId.getValue(), l2gwDeviceLocalMacs.size(), dpnId, elanName);
+    }
+
+    public static void installL2GwUcastMacInElan(EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, final ElanInstance elan,
+            L2GatewayDevice extL2GwDevice, final String macToBeAdded) {
+        final String extDeviceNodeId = extL2GwDevice.getHwvtepNodeId();
+        final String elanInstanceName = elan.getElanInstanceName();
+
+        // Retrieve all participating DPNs in this Elan. Populate this MAC in DMAC table.
+        // Looping through all DPNs in order to add/remove mac flows in their DMAC table
+        List<DpnInterfaces> elanDpns = ElanUtils.getInvolvedDpnsInElan(elanInstanceName);
+        for (DpnInterfaces elanDpn : elanDpns) {
+            final BigInteger dpnId = elanDpn.getDpId();
+            final String nodeId = getNodeIdFromDpnId(dpnId);
+
+            ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
+                    entityOwnershipService, MDSALUtil.NODE_PREFIX, nodeId);
+            Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
+                @Override
+                public void onSuccess(Boolean isOwner) {
+                    if (isOwner) {
+                        LOG.info("Installing DMAC flows in {} connected to cluster node owner", dpnId.toString());
+
+                        DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                        dataStoreCoordinator.enqueueJob(nodeId, new Callable<List<ListenableFuture<Void>>>() {
+                            @Override
+                            public List<ListenableFuture<Void>> call() throws Exception {
+                                return ElanUtils.installDmacFlowsToExternalRemoteMac(dpnId, extDeviceNodeId,
+                                        elan.getElanTag(), elan.getVni(), macToBeAdded, elanInstanceName);
+                            }
+                        }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+                    } else {
+                        LOG.info("Install DMAC flows is not executed on the cluster node as this is not owner " +
+                                    "for the DPN {}", dpnId.toString());
+                    }
+                }
+
+                @Override
+                public void onFailure(Throwable error) {
+                    LOG.error("Failed to install DMAC flows", error);
+                }
+            });
+        }
+
+        final IpAddress extL2GwDeviceTepIp = extL2GwDevice.getTunnelIp();
+        final List<PhysAddress> macList = new ArrayList<PhysAddress>();
+        macList.add(new PhysAddress(macToBeAdded));
+
+        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices =
+                ElanL2GwCacheUtils.getAllElanL2GatewayDevicesFromCache(elanInstanceName);
+        for (L2GatewayDevice otherDevice : elanL2GwDevices.values()) {
+            if (!otherDevice.getHwvtepNodeId().equals(extDeviceNodeId) && !areMLAGDevices(extL2GwDevice, otherDevice)) {
+                final String hwvtepId = otherDevice.getHwvtepNodeId();
+                InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId));
+                ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
+                        entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE,
+                        bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid));
+                Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
+                    @Override
+                    public void onSuccess(Boolean isOwner) {
+                        if (isOwner) {
+                            LOG.info("Adding DMAC entry in {} connected to cluster node owner", hwvtepId);
+
+                            DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                            dataStoreCoordinator.enqueueJob(hwvtepId, new Callable<List<ListenableFuture<Void>>>() {
+                                @Override
+                                public List<ListenableFuture<Void>> call() throws Exception {
+                                    final String logicalSwitchName = getLogicalSwitchFromElan(elanInstanceName);
+                                    ListenableFuture<Void> installFuture = installMacsInExternalDeviceAsRemoteUcastMacs(
+                                            hwvtepId, macList, logicalSwitchName, extL2GwDeviceTepIp);
+
+                                    Futures.addCallback(installFuture, new FutureCallback<Void>() {
+                                        @Override
+                                        public void onSuccess(Void noarg) {
+                                            if (LOG.isTraceEnabled()) {
+                                                LOG.trace("Successful in initiating ucast_remote_macs addition" +
+                                                        "related to {} in {}", logicalSwitchName, hwvtepId);
+                                            }
+                                        }
+
+                                        @Override
+                                        public void onFailure(Throwable error) {
+                                            LOG.error(String.format("Failed adding ucast_remote_macs related to " +
+                                                    "%s in %s", logicalSwitchName, hwvtepId), error);
+                                        }
+                                    });
+
+                                    return Lists.newArrayList(installFuture);
+                                }
+                            }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+                        } else {
+                            LOG.info("DMAC entry addition is not executed on the cluster node as this is not owner for " +
+                                    "the Hwvtep {}", hwvtepId);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable error) {
+                        LOG.error("Failed to install DMAC entry", error);
+                    }
+                });
+            }
+        }
+    }
+
+    public static void unInstallL2GwUcastMacFromElan(EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, final ElanInstance elan,
+            L2GatewayDevice extL2GwDevice, final LocalUcastMacs macToBeRemoved) {
+        final String extDeviceNodeId = extL2GwDevice.getHwvtepNodeId();
+        final String elanInstanceName = elan.getElanInstanceName();
+
+        // Retrieve all participating DPNs in this Elan. Populate this MAC in DMAC table.
+        // Looping through all DPNs in order to add/remove mac flows in their DMAC table
+        List<DpnInterfaces> elanDpns = ElanUtils.getInvolvedDpnsInElan(elanInstanceName);
+        for (DpnInterfaces elanDpn : elanDpns) {
+            final BigInteger dpnId = elanDpn.getDpId();
+            final String nodeId = getNodeIdFromDpnId(dpnId);
+
+            ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
+                    entityOwnershipService, MDSALUtil.NODE_PREFIX, nodeId);
+            Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
+                @Override
+                public void onSuccess(Boolean isOwner) {
+                    if (isOwner) {
+                        LOG.info("Uninstalling DMAC flows from {} connected to cluster node owner",
+                                dpnId.toString());
+
+                        DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                        dataStoreCoordinator.enqueueJob(nodeId, new Callable<List<ListenableFuture<Void>>>() {
+                            @Override
+                            public List<ListenableFuture<Void>> call() throws Exception {
+                                return ElanUtils.deleteDmacFlowsToExternalMac(elan.getElanTag(), dpnId,
+                                        extDeviceNodeId, macToBeRemoved.getMacEntryKey().getValue());
+                            }
+                        }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+                    } else {
+                        LOG.info("Uninstall DMAC flows is not executed on the cluster node as this is not owner " +
+                                    "for the DPN {}", dpnId.toString());
+                    }
+                }
+
+                @Override
+                public void onFailure(Throwable error) {
+                    LOG.error("Failed to uninstall DMAC flows", error);
+                }
+            });
+        }
+
+        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices =
+                ElanL2GwCacheUtils.getAllElanL2GatewayDevicesFromCache(elanInstanceName);
+        for (L2GatewayDevice otherDevice : elanL2GwDevices.values()) {
+            if (!otherDevice.getHwvtepNodeId().equals(extDeviceNodeId) && !areMLAGDevices(extL2GwDevice, otherDevice)) {
+                final String hwvtepId = otherDevice.getHwvtepNodeId();
+                final NodeId hwvtepNodeId = new NodeId(hwvtepId);
+                InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(hwvtepNodeId);
+                ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
+                        entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE,
+                        bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid));
+                Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
+                    @Override
+                    public void onSuccess(Boolean isOwner) {
+                        if (isOwner) {
+                            LOG.info("Removing DMAC entry from {} connected to cluster node owner", hwvtepId);
+
+                            DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                            dataStoreCoordinator.enqueueJob(hwvtepId, new Callable<List<ListenableFuture<Void>>>() {
+                                @Override
+                                public List<ListenableFuture<Void>> call() throws Exception {
+                                    final String logicalSwitchName = getLogicalSwitchFromElan(elanInstanceName);
+                                    ListenableFuture<Void> uninstallFuture = HwvtepUtils.deleteRemoteUcastMac(broker,
+                                            hwvtepNodeId, logicalSwitchName, macToBeRemoved.getMacEntryKey());
+
+                                    Futures.addCallback(uninstallFuture, new FutureCallback<Void>() {
+                                        @Override
+                                        public void onSuccess(Void noarg) {
+                                            if (LOG.isTraceEnabled()) {
+                                                LOG.trace("Successful in initiating ucast_remote_macs deletion " +
+                                                        "related to {} in {}", logicalSwitchName, hwvtepId);
+                                            }
+                                        }
+
+                                        @Override
+                                        public void onFailure(Throwable error) {
+                                            LOG.error(String.format("Failed removing ucast_remote_macs related " +
+                                                    "to %s in %s", logicalSwitchName, hwvtepId), error);
+                                        }
+                                    });
+
+                                    return Lists.newArrayList(uninstallFuture);
+                                }
+                            }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+                        } else {
+                            LOG.info("DMAC entry removal is not executed on the cluster node as this is not owner for " +
+                                    "the Hwvtep {}", hwvtepId);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable error) {
+                        LOG.error("Failed to uninstall DMAC entry", error);
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     * Delete elan macs from L2 gateway device.<br>
+     * This includes deleting ELAN mac table entries plus external device
+     * UcastLocalMacs which are part of the same ELAN.
+     *
+     * @param l2GatewayDevice
+     *            the l2 gateway device
+     * @param elanName
+     *            the elan name
+     * @return the listenable future
+     */
+    public static ListenableFuture<Void> deleteElanMacsFromL2GatewayDevice(L2GatewayDevice l2GatewayDevice,
+            String elanName) {
+        List<MacAddress> elanMacTableEntries = getElanMacTableEntries(elanName);
+        List<MacAddress> elanL2GatewayDevicesLocalMacs = getElanL2GatewayDevicesLocalMacs(l2GatewayDevice, elanName);
+
+        List<MacAddress> lstElanLocalMacs = new ArrayList<>(elanMacTableEntries);
+        lstElanLocalMacs.addAll(elanL2GatewayDevicesLocalMacs);
+
+        return HwvtepUtils.deleteRemoteUcastMacs(broker, new NodeId(l2GatewayDevice.getHwvtepNodeId()),
+                elanName, lstElanLocalMacs);
+    }
+
+    /**
+     * Gets the elan mac table entries.
+     *
+     * @param elanName
+     *            the elan name
+     * @return the elan mac table entries as list
+     */
+    public static List<MacAddress> getElanMacTableEntries(String elanName) {
+        MacTable macTable = ElanUtils.getElanMacTable(elanName);
+        if (macTable == null || macTable.getMacEntry() == null || macTable.getMacEntry().isEmpty()) {
+            LOG.trace("MacTable is empty for elan: {}", elanName);
+            return Collections.emptyList();
+        }
+        List<MacAddress> lstMacs = Lists.transform(macTable.getMacEntry(), new Function<MacEntry, MacAddress>() {
+            @Override
+            public MacAddress apply(MacEntry macEntry) {
+                return (macEntry != null) ? new MacAddress(macEntry.getMacAddress().getValue()) : null;
+            }
+        });
+        return lstMacs;
+    }
+
+    /**
+     * Gets the elan l2 gateway devices local macs.
+     *
+     * @param l2GwDeviceToBeExcluded
+     *            the l2 gw device to be excluded
+     * @param elanName
+     *            the elan name
+     * @return the elan l2 gateway devices local macs
+     */
+    public static List<MacAddress> getElanL2GatewayDevicesLocalMacs(L2GatewayDevice l2GwDeviceToBeExcluded,
+            String elanName) {
+        List<MacAddress> lstL2GatewayDeviceMacs = new ArrayList<>();
+
+        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevicesFromCache = ElanL2GwCacheUtils
+                .getAllElanL2GatewayDevicesFromCache(elanName);
+        if (elanL2GwDevicesFromCache != null) {
+            for (L2GatewayDevice otherDevice : elanL2GwDevicesFromCache.values()) {
+                if (!otherDevice.getHwvtepNodeId().equals(l2GwDeviceToBeExcluded.getHwvtepNodeId())) {
+                    List<LocalUcastMacs> lstUcastLocalMacs = otherDevice.getUcastLocalMacs();
+                    if (lstUcastLocalMacs != null) {
+                        List<MacAddress> l2GwDeviceMacs = Lists.transform(lstUcastLocalMacs,
+                                new Function<LocalUcastMacs, MacAddress>() {
+                                    @Override
+                                    public MacAddress apply(LocalUcastMacs localUcastMac) {
+                                        return (localUcastMac != null) ? localUcastMac.getMacEntryKey() : null;
+                                    }
+                                });
+                        lstL2GatewayDeviceMacs.addAll(l2GwDeviceMacs);
+                    }
+                }
+            }
+        }
+        return lstL2GatewayDeviceMacs;
+    }
+
+    /**
+     * Install ELAN macs in L2 Gateway device.<br>
+     * This includes installing ELAN mac table entries plus external device
+     * UcastLocalMacs which are part of the same ELAN.
+     *
+     * @param elanName
+     *            the elan name
+     * @param l2GatewayDevice
+     *            the l2 gateway device which has to be configured
+     * @return the listenable future
+     */
+    public static ListenableFuture<Void> installElanMacsInL2GatewayDevice(String elanName,
+            L2GatewayDevice l2GatewayDevice) {
+        String logicalSwitchName = getLogicalSwitchFromElan(elanName);
+        NodeId hwVtepNodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId());
+
+        List<RemoteUcastMacs> lstL2GatewayDevicesMacs = getL2GatewayDevicesUcastLocalMacsAsRemoteUcastMacs(elanName,
+                l2GatewayDevice, hwVtepNodeId, logicalSwitchName);
+        List<RemoteUcastMacs> lstElanMacTableEntries = getElanMacTableEntriesAsRemoteUcastMacs(elanName,
+                l2GatewayDevice, hwVtepNodeId, logicalSwitchName);
+
+        List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<>(lstL2GatewayDevicesMacs);
+        lstRemoteUcastMacs.addAll(lstElanMacTableEntries);
+
+        ListenableFuture<Void> future = HwvtepUtils.addRemoteUcastMacs(broker, hwVtepNodeId, lstRemoteUcastMacs);
+
+        LOG.info("Added RemoteUcastMacs entries [{}] in config DS. NodeID: {}, LogicalSwitch: {}",
+                lstRemoteUcastMacs.size(), hwVtepNodeId.getValue(), logicalSwitchName);
+        return future;
+    }
+
+    /**
+     * Gets the l2 gateway devices ucast local macs as remote ucast macs.
+     *
+     * @param elanName
+     *            the elan name
+     * @param l2GatewayDeviceToBeConfigured
+     *            the l2 gateway device to be configured
+     * @param hwVtepNodeId
+     *            the hw vtep node Id to be configured
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @return the l2 gateway devices macs as remote ucast macs
+     */
+    public static List<RemoteUcastMacs> getL2GatewayDevicesUcastLocalMacsAsRemoteUcastMacs(String elanName,
+            L2GatewayDevice l2GatewayDeviceToBeConfigured, NodeId hwVtepNodeId, String logicalSwitchName) {
+        List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<RemoteUcastMacs>();
+        ConcurrentMap<String, L2GatewayDevice> elanL2GwDevicesFromCache = ElanL2GwCacheUtils
+                .getAllElanL2GatewayDevicesFromCache(elanName);
+
+        if (elanL2GwDevicesFromCache != null) {
+            for (L2GatewayDevice otherDevice : elanL2GwDevicesFromCache.values()) {
+                if (l2GatewayDeviceToBeConfigured.getHwvtepNodeId().equals(otherDevice.getHwvtepNodeId())) {
+                    continue;
+                }
+                if (!areMLAGDevices(l2GatewayDeviceToBeConfigured, otherDevice)) {
+                    List<LocalUcastMacs> lstUcastLocalMacs = otherDevice.getUcastLocalMacs();
+                    if (lstUcastLocalMacs != null) {
+                        for (LocalUcastMacs localUcastMac : lstUcastLocalMacs) {
+                            HwvtepPhysicalLocatorAugmentation physLocatorAug = HwvtepSouthboundUtils
+                                    .createHwvtepPhysicalLocatorAugmentation(
+                                            String.valueOf(otherDevice.getTunnelIp().getValue()));
+                            RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(hwVtepNodeId,
+                                    localUcastMac.getMacEntryKey().getValue(), localUcastMac.getIpaddr(),
+                                    logicalSwitchName, physLocatorAug);
+                            lstRemoteUcastMacs.add(remoteUcastMac);
+                        }
+                    }
+                }
+            }
+        }
+        return lstRemoteUcastMacs;
+    }
+
+    /**
+     * Are MLAG devices.
+     *
+     * @param l2GatewayDevice
+     *            the l2 gateway device
+     * @param otherL2GatewayDevice
+     *            the other l2 gateway device
+     * @return true, if both the specified l2 gateway devices are part of same
+     *         MLAG
+     */
+    public static boolean areMLAGDevices(L2GatewayDevice l2GatewayDevice, L2GatewayDevice otherL2GatewayDevice) {
+        // If tunnel IPs are same, then it is considered to be part of same MLAG
+        return Objects.equals(l2GatewayDevice.getTunnelIp(), otherL2GatewayDevice.getTunnelIp());
+    }
+
+    /**
+     * Gets the elan mac table entries as remote ucast macs. <br>
+     * Note: ELAN MAC table only contains internal switches MAC's. It doesn't
+     * contain external device MAC's.
+     *
+     * @param elanName
+     *            the elan name
+     * @param l2GatewayDeviceToBeConfigured
+     *            the l2 gateway device to be configured
+     * @param hwVtepNodeId
+     *            the hw vtep node id
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @return the elan mac table entries as remote ucast macs
+     */
+    public static List<RemoteUcastMacs> getElanMacTableEntriesAsRemoteUcastMacs(String elanName,
+            L2GatewayDevice l2GatewayDeviceToBeConfigured, NodeId hwVtepNodeId, String logicalSwitchName) {
+        List<RemoteUcastMacs> lstRemoteUcastMacs = new ArrayList<RemoteUcastMacs>();
+
+        MacTable macTable = ElanUtils.getElanMacTable(elanName);
+        if (macTable == null || macTable.getMacEntry() == null || macTable.getMacEntry().isEmpty()) {
+            LOG.trace("MacTable is empty for elan: {}", elanName);
+            return lstRemoteUcastMacs;
+        }
+
+        for (MacEntry macEntry : macTable.getMacEntry()) {
+            BigInteger dpnId = ElanUtils.getDpidFromInterface(macEntry.getInterface());
+            if (dpnId == null) {
+                LOG.error("DPN ID not found for interface {}", macEntry.getInterface());
+                continue;
+            }
+
+            IpAddress dpnTepIp = getSourceDpnTepIp(dpnId, hwVtepNodeId);
+            LOG.trace("Dpn Tep IP: {} for dpnId: {} and nodeId: {}", dpnTepIp, dpnId, hwVtepNodeId.getValue());
+            if (dpnTepIp == null) {
+                LOG.error("TEP IP not found for dpnId {} and nodeId {}", dpnId, hwVtepNodeId.getValue());
+                continue;
+            }
+            HwvtepPhysicalLocatorAugmentation physLocatorAug = HwvtepSouthboundUtils
+                    .createHwvtepPhysicalLocatorAugmentation(String.valueOf(dpnTepIp.getValue()));
+            // TODO: Query ARP cache to get IP address corresponding to the
+            // MAC
+            IpAddress ipAddress = null;
+            RemoteUcastMacs remoteUcastMac = HwvtepSouthboundUtils.createRemoteUcastMac(hwVtepNodeId,
+                    macEntry.getMacAddress().getValue(), ipAddress, logicalSwitchName, physLocatorAug);
+            lstRemoteUcastMacs.add(remoteUcastMac);
+        }
+        return lstRemoteUcastMacs;
+    }
+
+    /**
+     * Gets the external tunnel interface name.
+     *
+     * @param sourceNode
+     *            the source node
+     * @param dstNode
+     *            the dst node
+     * @return the external tunnel interface name
+     */
+    public static String getExternalTunnelInterfaceName(String sourceNode, String dstNode) {
+        String tunnelInterfaceName = null;
+        try {
+            Future<RpcResult<GetExternalTunnelInterfaceNameOutput>> output = itmRpcService
+                    .getExternalTunnelInterfaceName(new GetExternalTunnelInterfaceNameInputBuilder()
+                            .setSourceNode(sourceNode).setDestinationNode(dstNode).build());
+
+            RpcResult<GetExternalTunnelInterfaceNameOutput> rpcResult = output.get();
+            if (rpcResult.isSuccessful()) {
+                tunnelInterfaceName = rpcResult.getResult().getInterfaceName();
+                LOG.debug("Tunnel interface name: {} for sourceNode: {} and dstNode: {}", tunnelInterfaceName,
+                        sourceNode, dstNode);
+            } else {
+                LOG.warn("RPC call to ITM.GetExternalTunnelInterfaceName failed with error: {}",
+                        rpcResult.getErrors());
+            }
+        } catch (NullPointerException | InterruptedException | ExecutionException e) {
+            LOG.error("Failed to get external tunnel interface name for sourceNode: {} and dstNode: {}: {} ",
+                    sourceNode, dstNode, e);
+        }
+        return tunnelInterfaceName;
+    }
+
+    /**
+     * Gets the source dpn tep ip.
+     *
+     * @param srcDpnId
+     *            the src dpn id
+     * @param dstHwVtepNodeId
+     *            the dst hw vtep node id
+     * @return the dpn tep ip
+     */
+    public static IpAddress getSourceDpnTepIp(BigInteger srcDpnId, NodeId dstHwVtepNodeId) {
+        IpAddress dpnTepIp = null;
+        String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(srcDpnId),
+                dstHwVtepNodeId.getValue());
+        if (tunnelInterfaceName != null) {
+            Interface tunnelInterface = getInterfaceFromConfigDS(new InterfaceKey(tunnelInterfaceName), broker);
+            if (tunnelInterface != null) {
+                dpnTepIp = tunnelInterface.getAugmentation(IfTunnel.class).getTunnelSource();
+            } else {
+                LOG.warn("Tunnel interface not found for tunnelInterfaceName {}", tunnelInterfaceName);
+            }
+        } else {
+            LOG.warn("Tunnel interface name not found for srcDpnId {} and dstHwVtepNodeId {}", srcDpnId,
+                    dstHwVtepNodeId);
+        }
+        return dpnTepIp;
+    }
+
+    /**
+     * Update vlan bindings in l2 gateway device.
+     *
+     * @param nodeId
+     *            the node id
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @param hwVtepDevice
+     *            the hardware device
+     * @param defaultVlanId
+     *            the default vlan id
+     * @return the listenable future
+     */
+    public static ListenableFuture<Void> updateVlanBindingsInL2GatewayDevice(NodeId nodeId, String logicalSwitchName,
+            Devices hwVtepDevice, Integer defaultVlanId) {
+        if (hwVtepDevice == null || hwVtepDevice.getInterfaces() == null || hwVtepDevice.getInterfaces().isEmpty()) {
+            String errMsg = "HwVtepDevice is null or interfaces are empty.";
+            LOG.error(errMsg);
+            return Futures.immediateFailedFuture(new RuntimeException(errMsg));
+        }
+
+        WriteTransaction transaction = broker.newWriteOnlyTransaction();
+        for (org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.devices.Interfaces deviceInterface : hwVtepDevice
+                .getInterfaces()) {
+            List<VlanBindings> vlanBindings = new ArrayList<>();
+            if (deviceInterface.getSegmentationIds() != null && !deviceInterface.getSegmentationIds().isEmpty()) {
+                for (Integer vlanId : deviceInterface.getSegmentationIds()) {
+                    vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(nodeId, vlanId, logicalSwitchName));
+                }
+            } else {
+                // Use defaultVlanId (specified in L2GatewayConnection) if Vlan
+                // ID not specified at interface level.
+                vlanBindings.add(HwvtepSouthboundUtils.createVlanBinding(nodeId, defaultVlanId, logicalSwitchName));
+            }
+            HwvtepUtils.mergeVlanBindings(transaction, nodeId, hwVtepDevice.getDeviceName(),
+                    deviceInterface.getInterfaceName(), vlanBindings);
+        }
+        ListenableFuture<Void> future = transaction.submit();
+        LOG.info("Updated Hwvtep VlanBindings in config DS. NodeID: {}, LogicalSwitch: {}", nodeId.getValue(),
+                logicalSwitchName);
+        return future;
+    }
+
+    /**
+     * Delete vlan bindings from l2 gateway device.
+     *
+     * @param nodeId
+     *            the node id
+     * @param hwVtepDevice
+     *            the hw vtep device
+     * @param defaultVlanId
+     *            the default vlan id
+     * @return the listenable future
+     */
+    public static ListenableFuture<Void> deleteVlanBindingsFromL2GatewayDevice(NodeId nodeId, Devices hwVtepDevice,
+            Integer defaultVlanId) {
+        if (hwVtepDevice == null || hwVtepDevice.getInterfaces() == null || hwVtepDevice.getInterfaces().isEmpty()) {
+            String errMsg = "HwVtepDevice is null or interfaces are empty.";
+            LOG.error(errMsg);
+            return Futures.immediateFailedFuture(new RuntimeException(errMsg));
+        }
+        NodeId physicalSwitchNodeId = HwvtepSouthboundUtils.createManagedNodeId(nodeId, hwVtepDevice.getDeviceName());
+
+        WriteTransaction transaction = broker.newWriteOnlyTransaction();
+        for (org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.devices.Interfaces deviceInterface : hwVtepDevice
+                .getInterfaces()) {
+            String phyPortName = deviceInterface.getInterfaceName();
+            if (deviceInterface.getSegmentationIds() != null && !deviceInterface.getSegmentationIds().isEmpty()) {
+                for (Integer vlanId : deviceInterface.getSegmentationIds()) {
+                    HwvtepUtils.deleteVlanBinding(transaction, physicalSwitchNodeId, phyPortName, vlanId);
+                }
+            } else {
+                // Use defaultVlanId (specified in L2GatewayConnection) if Vlan
+                // ID not specified at interface level.
+                HwvtepUtils.deleteVlanBinding(transaction, physicalSwitchNodeId, phyPortName, defaultVlanId);
+            }
+        }
+        ListenableFuture<Void> future = transaction.submit();
+
+        LOG.info("Deleted Hwvtep VlanBindings from config DS. NodeID: {}, hwVtepDevice: {}, defaultVlanId: {} ",
+                nodeId.getValue(), hwVtepDevice, defaultVlanId);
+        return future;
+    }
+
+    /**
+     * Gets the elan name from logical switch name.
+     *
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @return the elan name from logical switch name
+     */
+    public static String getElanFromLogicalSwitch(String logicalSwitchName) {
+        // Assuming elan name is same as logical switch name
+        String elanName = logicalSwitchName;
+        return elanName;
+    }
+
+    /**
+     * Gets the logical switch name from elan name.
+     *
+     * @param elanName
+     *            the elan name
+     * @return the logical switch from elan name
+     */
+    public static String getLogicalSwitchFromElan(String elanName) {
+        // Assuming logical switch name is same as elan name
+        String logicalSwitchName = elanName;
+        return logicalSwitchName;
+    }
+
+    /**
+     * Gets the l2 gateway connection job key.
+     *
+     * @param nodeId
+     *            the node id
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @return the l2 gateway connection job key
+     */
+    public static String getL2GatewayConnectionJobKey(String nodeId, String logicalSwitchName) {
+        return new StringBuilder(nodeId).append(logicalSwitchName).toString();
+    }
+
+    public static InstanceIdentifier<Interface> getInterfaceIdentifier(InterfaceKey interfaceKey) {
+        InstanceIdentifier.InstanceIdentifierBuilder<Interface> interfaceInstanceIdentifierBuilder =
+                InstanceIdentifier.builder(Interfaces.class).child(Interface.class, interfaceKey);
+        return interfaceInstanceIdentifierBuilder.build();
+    }
+
+    public static Interface getInterfaceFromConfigDS(InterfaceKey interfaceKey, DataBroker dataBroker) {
+        InstanceIdentifier<Interface> interfaceId = getInterfaceIdentifier(interfaceKey);
+        Optional<Interface> interfaceOptional = IfmUtil.read(LogicalDatastoreType.CONFIGURATION, interfaceId, dataBroker);
+        if (!interfaceOptional.isPresent()) {
+            return null;
+        }
+
+        return interfaceOptional.get();
+    }
+
+    /**
+     * Delete l2 gateway device ucast local macs from elan.<br>
+     * Deletes macs from internal ELAN nodes and also on rest of external l2
+     * gateway devices which are part of the ELAN.
+     *
+     * @param l2GatewayDevice
+     *            the l2 gateway device whose ucast local macs to be deleted
+     *            from elan
+     * @param elanName
+     *            the elan name
+     * @return the listenable future
+     */
+    public static List<ListenableFuture<Void>> deleteL2GatewayDeviceUcastLocalMacsFromElan(
+            L2GatewayDevice l2GatewayDevice, String elanName) {
+        List<ListenableFuture<Void>> futures = new ArrayList<>();
+
+        ElanInstance elan = ElanUtils.getElanInstanceByName(elanName);
+        if (elan == null) {
+            LOG.error("Could not find Elan by name: {}", elanName);
+            return futures;
+        }
+
+        List<LocalUcastMacs> lstLocalUcastMacs = l2GatewayDevice.getUcastLocalMacs();
+        if (lstLocalUcastMacs != null) {
+            for (LocalUcastMacs localUcastMac : lstLocalUcastMacs) {
+                List<DpnInterfaces> dpnInterfaces = ElanUtils.getInvolvedDpnsInElan(elanName);
+                if (dpnInterfaces != null) {
+                    // TODO: Need to check if it can be optimized
+                    for (DpnInterfaces elanDpn : dpnInterfaces) {
+                        ElanUtils.deleteDmacFlowsToExternalMac(elan.getElanTag(), elanDpn.getDpId(),
+                                l2GatewayDevice.getHwvtepNodeId(), localUcastMac.getMacEntryKey().getValue());
+                    }
+                }
+            }
+
+            List<MacAddress> lstMac = Lists.transform(lstLocalUcastMacs, new Function<LocalUcastMacs, MacAddress>() {
+                @Override
+                public MacAddress apply(LocalUcastMacs mac) {
+                    return (mac != null) ? mac.getMacEntryKey() : null;
+                }
+            });
+
+            ConcurrentMap<String, L2GatewayDevice> elanL2GwDevices = ElanL2GwCacheUtils
+                    .getAllElanL2GatewayDevicesFromCache(elanName);
+            for (L2GatewayDevice otherDevice : elanL2GwDevices.values()) {
+                if (!otherDevice.getHwvtepNodeId().equals(l2GatewayDevice.getHwvtepNodeId())) {
+                    futures.add(HwvtepUtils.deleteRemoteUcastMacs(broker, new NodeId(otherDevice.getHwvtepNodeId()),
+                            elanName, lstMac));
+                }
+            }
+        }
+        return futures;
+    }
+
+    public static void createItmTunnels(ItmRpcService itmRpcService, String hwvtepId, String psName,
+            IpAddress tunnelIp) {
+        AddL2GwDeviceInputBuilder builder = new AddL2GwDeviceInputBuilder();
+        builder.setTopologyId(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID.getValue());
+        builder.setNodeId(HwvtepSouthboundUtils.createManagedNodeId(new NodeId(hwvtepId), psName).getValue());
+        builder.setIpAddress(tunnelIp);
+        try {
+            Future<RpcResult<Void>> result = itmRpcService.addL2GwDevice(builder.build());
+            RpcResult<Void> rpcResult = result.get();
+            if (rpcResult.isSuccessful()) {
+                LOG.info("Created ITM tunnels for {}", hwvtepId);
+            } else {
+                LOG.error("Failed to create ITM Tunnels: ", rpcResult.getErrors());
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("RPC to create ITM tunnels failed", e);
+        }
+    }
+
+    public static String getNodeIdFromDpnId(BigInteger dpnId) {
+        return MDSALUtil.NODE_PREFIX + MDSALUtil.SEPARATOR + dpnId.toString();
+    }
+
+}
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/L2GatewayConnectionUtils.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/L2GatewayConnectionUtils.java
new file mode 100644 (file)
index 0000000..be0012f
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * 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.vpnservice.elan.l2gw.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils;
+import org.opendaylight.vpnservice.datastoreutils.DataStoreJobCoordinator;
+import org.opendaylight.vpnservice.elan.internal.ElanInstanceManager;
+import org.opendaylight.vpnservice.elan.l2gw.listeners.HwvtepLogicalSwitchListener;
+import org.opendaylight.vpnservice.elan.l2gw.listeners.HwvtepRemoteMcastMacListener;
+import org.opendaylight.vpnservice.elan.utils.ElanUtils;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice;
+import org.opendaylight.vpnservice.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils;
+import org.opendaylight.vpnservice.utils.SystemPropertyReader;
+import org.opendaylight.vpnservice.utils.clustering.ClusteringUtils;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundConstants;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundUtils;
+import org.opendaylight.vpnservice.utils.hwvtep.HwvtepUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.attributes.Devices;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.L2gatewayConnections;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.L2gateways;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.l2gateways.L2gateway;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateways.attributes.l2gateways.L2gatewayKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.instances.ElanInstance;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class L2GatewayConnectionUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(L2GatewayConnectionUtils.class);
+
+    public static boolean isGatewayAssociatedToL2Device(L2GatewayDevice l2GwDevice) {
+        return (l2GwDevice.getL2GatewayIds().size() > 0);
+    }
+
+    public static L2gateway getNeutronL2gateway(DataBroker broker, Uuid l2GatewayId) {
+        LOG.debug("getNeutronL2gateway for {}", l2GatewayId.getValue());
+        InstanceIdentifier<L2gateway> inst = InstanceIdentifier.create(Neutron.class).child(L2gateways.class)
+                .child(L2gateway.class, new L2gatewayKey(l2GatewayId));
+        Optional<L2gateway> l2Gateway = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst);
+        if (l2Gateway.isPresent()) {
+            return l2Gateway.get();
+        }
+        return null;
+    }
+
+    public static List<L2gateway> getL2gatewayList(DataBroker broker) {
+        InstanceIdentifier<L2gateways> inst = InstanceIdentifier.create(Neutron.class).child(L2gateways.class);
+        Optional<L2gateways> l2gateways = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst);
+
+        if (l2gateways.isPresent()) {
+            return l2gateways.get().getL2gateway();
+        }
+        return null;
+    }
+
+    public static List<L2gatewayConnection> getAllL2gatewayConnections(DataBroker broker) {
+        InstanceIdentifier<L2gatewayConnections> inst = InstanceIdentifier.create(Neutron.class)
+                .child(L2gatewayConnections.class);
+        Optional<L2gatewayConnections> l2GwConns = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst);
+        if (l2GwConns.isPresent()) {
+            return l2GwConns.get().getL2gatewayConnection();
+        }
+        return null;
+    }
+
+    public static void addL2GatewayConnection(DataBroker broker, EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, ElanInstanceManager elanInstanceManager,
+            L2gatewayConnection input) {
+        addL2GatewayConnection(broker, entityOwnershipService, bindingNormalizedNodeSerializer, elanInstanceManager,
+                input, null);
+    }
+
+    public static void addL2GatewayConnection(DataBroker broker, EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, ElanInstanceManager elanInstanceManager,
+            L2gatewayConnection input, String l2GwDeviceName) {
+        Uuid networkUuid = input.getNetworkId();
+        ElanInstance elanInstance = elanInstanceManager.getElanInstanceByName(networkUuid.getValue());
+        if (elanInstance == null || elanInstance.getVni() == null) {
+            LOG.error("Neutron network with id {} is not present", networkUuid.getValue());
+        } else {
+            Uuid l2GatewayId = input.getL2gatewayId();
+            L2gateway l2Gateway = getNeutronL2gateway(broker, l2GatewayId);
+            if (l2Gateway == null) {
+                LOG.error("L2Gateway with id {} is not present", l2GatewayId.getValue());
+            } else {
+                associateHwvtepsToElan(broker, entityOwnershipService, bindingNormalizedNodeSerializer, elanInstance,
+                        l2Gateway, input.getSegmentId(), l2GwDeviceName);
+            }
+        }
+    }
+
+    public static void deleteL2GatewayConnection(DataBroker broker, EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, ElanInstanceManager elanInstanceManager,
+            L2gatewayConnection input) {
+        Uuid networkUuid = input.getNetworkId();
+        ElanInstance elanInstance = elanInstanceManager.getElanInstanceByName(networkUuid.getValue());
+        if (elanInstance == null) {
+            LOG.error("Neutron network with id {} is not present", networkUuid.getValue());
+        } else {
+            Uuid l2GatewayId = input.getL2gatewayId();
+            L2gateway l2Gateway = L2GatewayConnectionUtils.getNeutronL2gateway(broker, l2GatewayId);
+            if (l2Gateway == null) {
+                LOG.error("L2Gateway with id {} is not present", l2GatewayId.getValue());
+            } else {
+                disAssociateHwvtepsToElan(broker, entityOwnershipService, bindingNormalizedNodeSerializer, elanInstance,
+                        l2Gateway, input.getSegmentId());
+            }
+        }
+    }
+
+    private static void disAssociateHwvtepsToElan(final DataBroker broker, EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, final ElanInstance elanInstance,
+            L2gateway l2Gateway, final Integer defaultVlan) {
+        final String elanName = elanInstance.getElanInstanceName();
+        List<Devices> l2Devices = l2Gateway.getDevices();
+        for (final Devices l2Device : l2Devices) {
+            final String l2DeviceName = l2Device.getDeviceName();
+            final L2GatewayDevice l2GatewayDevice = L2GatewayCacheUtils.getL2DeviceFromCache(l2DeviceName);
+            if (isL2GwDeviceConnected(l2GatewayDevice)) {//TODO handle delete while device is offline
+                // Delete L2 Gateway device from 'ElanL2GwDevice' cache
+                ElanL2GwCacheUtils.removeL2GatewayDeviceFromCache(elanName, l2GatewayDevice.getHwvtepNodeId());
+
+                final String hwvtepId = l2GatewayDevice.getHwvtepNodeId();
+                InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId));
+                ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
+                        entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE,
+                        bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid));
+                Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
+                    @Override
+                    public void onSuccess(Boolean isOwner) {
+                        if (isOwner) {
+                            LOG.info("L2 Gateway device delete is triggered for {} connected to cluster owner node",
+                                    l2DeviceName);
+
+                            // Create DataStoreJobCoordinator jobs to create Logical
+                            // switches on all physical switches
+                            // which are part of L2 Gateway
+                            DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                            DisAssociateHwvtepFromElan disAssociateHwvtepToElan = new DisAssociateHwvtepFromElan(broker,
+                                    l2GatewayDevice, elanInstance, l2Device, defaultVlan);
+                            String jobKey = ElanL2GatewayUtils.getL2GatewayConnectionJobKey(hwvtepId, elanName);
+                            dataStoreCoordinator.enqueueJob(jobKey, disAssociateHwvtepToElan,
+                                    SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+                        } else {
+                            LOG.info("L2 Gateway device delete is not triggered on the cluster node as this is not " +
+                                    "owner for {}", l2DeviceName);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable error) {
+                        LOG.error("Failed to trigger L2 Gateway device delete action", error);
+                    }
+                });
+            } else {
+                LOG.error("could not handle connection delete L2 Gateway device with id {} is not present",
+                        l2DeviceName);
+            }
+        }
+    }
+
+    private static void associateHwvtepsToElan(final DataBroker broker, EntityOwnershipService entityOwnershipService,
+            BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer, final ElanInstance elanInstance,
+            L2gateway l2Gateway, final Integer defaultVlan, String l2GwDeviceName) {
+        final String elanName = elanInstance.getElanInstanceName();
+        List<Devices> l2Devices = l2Gateway.getDevices();
+        for (final Devices l2Device : l2Devices) {
+            final String l2DeviceName = l2Device.getDeviceName();
+            // L2gateway can have more than one L2 Gw devices. Configure Logical Switch, VLAN mappings,...
+            // only on the switch which has come up just now and exclude all other devices from
+            // preprovisioning/re-provisioning
+            if (l2GwDeviceName != null && !l2GwDeviceName.equals(l2DeviceName)) {
+                continue;
+            }
+            final L2GatewayDevice l2GatewayDevice = L2GatewayCacheUtils.getL2DeviceFromCache(l2DeviceName);
+            if (isL2GwDeviceConnected(l2GatewayDevice)) {
+                // Add L2 Gateway device to 'ElanL2GwDevice' cache
+                final boolean createLogicalSwitch;
+                LogicalSwitches logicalSwitch = HwvtepUtils.getLogicalSwitch(broker, LogicalDatastoreType.OPERATIONAL,
+                        new NodeId(l2GatewayDevice.getHwvtepNodeId()), elanName);
+                if (logicalSwitch == null) {
+                    final HwvtepLogicalSwitchListener hwVTEPLogicalSwitchListener = new HwvtepLogicalSwitchListener(
+                            l2GatewayDevice, elanName, l2Device, defaultVlan);
+                    hwVTEPLogicalSwitchListener.registerListener(LogicalDatastoreType.OPERATIONAL, broker);
+                    createLogicalSwitch = true;
+                } else {
+                    addL2DeviceToElanL2GwCache(elanName, l2GatewayDevice);
+                    createLogicalSwitch = false;
+                }
+                final String hwvtepId = l2GatewayDevice.getHwvtepNodeId();
+                InstanceIdentifier<Node> iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId));
+                ListenableFuture<Boolean> checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner(
+                        entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE,
+                        bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid));
+                Futures.addCallback(checkEntityOwnerFuture, new FutureCallback<Boolean>() {
+                    @Override
+                    public void onSuccess(Boolean isOwner) {
+                        if (isOwner) {
+                            LOG.info("Creating Logical switch on {} connected to cluster owner node", l2DeviceName);
+
+                            // Create DataStoreJobCoordinator jobs to create Logical
+                            // switches on all physical switches
+                            // which are part of L2 Gateway
+                            DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                            AssociateHwvtepToElan associateHwvtepToElan = new AssociateHwvtepToElan(broker,
+                                    l2GatewayDevice, elanInstance, l2Device, defaultVlan, createLogicalSwitch);
+                            String jobKey = ElanL2GatewayUtils.getL2GatewayConnectionJobKey(hwvtepId, elanName);
+                            dataStoreCoordinator.enqueueJob(jobKey, associateHwvtepToElan,
+                                    SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
+                        } else {
+                            LOG.info("Logical switch creation is not triggered on the cluster node as this is not " +
+                                    "owner for {}", l2DeviceName);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable error) {
+                        LOG.error("Failed to trigger Logical switch creation action", error);
+                    }
+                });
+            } else {
+                LOG.error("L2 Gateway device with id {} is not present", l2DeviceName);
+            }
+        }
+    }
+
+    public static void addL2DeviceToElanL2GwCache(String elanName, L2GatewayDevice l2GatewayDevice) {
+        L2GatewayDevice elanL2GwDevice = new L2GatewayDevice();
+        elanL2GwDevice.setHwvtepNodeId(l2GatewayDevice.getHwvtepNodeId());
+        elanL2GwDevice.setDeviceName(l2GatewayDevice.getDeviceName());
+        elanL2GwDevice.setTunnelIps(l2GatewayDevice.getTunnelIps());
+        ElanL2GwCacheUtils.addL2GatewayDeviceToCache(elanName, elanL2GwDevice);
+    }
+
+    private static boolean isL2GwDeviceConnected(L2GatewayDevice l2GwDevice) {
+        return (l2GwDevice != null && l2GwDevice.getHwvtepNodeId() != null && l2GwDevice.isConnected());
+    }
+
+    private static class AssociateHwvtepToElan implements Callable<List<ListenableFuture<Void>>> {
+        DataBroker broker;
+        L2GatewayDevice l2GatewayDevice;
+        ElanInstance elanInstance;
+        Devices l2Device;
+        Integer defaultVlan;
+        boolean createLogicalSwitch;
+
+        public AssociateHwvtepToElan(DataBroker broker, L2GatewayDevice l2GatewayDevice, ElanInstance elanInstance,
+                Devices l2Device, Integer defaultVlan, boolean createLogicalSwitch) {
+            this.broker = broker;
+            this.l2GatewayDevice = l2GatewayDevice;
+            this.elanInstance = elanInstance;
+            this.l2Device = l2Device;
+            this.defaultVlan = defaultVlan;
+            this.createLogicalSwitch = createLogicalSwitch;
+        }
+
+        @Override
+        public List<ListenableFuture<Void>> call() throws Exception {
+            List<ListenableFuture<Void>> futures = new ArrayList<>();
+
+            final String logicalSwitchName = ElanL2GatewayUtils.getLogicalSwitchFromElan(elanInstance.getElanInstanceName());
+
+            // Create Logical Switch if it's not created already in
+            // the device
+            if (createLogicalSwitch) {
+                ListenableFuture<Void> lsCreateFuture = createLogicalSwitch(l2GatewayDevice, elanInstance, l2Device);
+                futures.add(lsCreateFuture);
+            } else {
+                // Logical switch is already created; do the rest of
+                // configuration
+                futures.add(ElanL2GatewayUtils.updateVlanBindingsInL2GatewayDevice(
+                        new NodeId(l2GatewayDevice.getHwvtepNodeId()), logicalSwitchName, l2Device, defaultVlan));
+                futures.add(ElanL2GatewayMulticastUtils.handleMcastForElanL2GwDeviceAdd(logicalSwitchName, l2GatewayDevice));
+                HwvtepRemoteMcastMacListener list = new HwvtepRemoteMcastMacListener(ElanUtils.getDataBroker(),
+                        logicalSwitchName, l2GatewayDevice,
+                        new Callable<List<ListenableFuture<Void>>>() {
+
+                        @Override
+                        public List<ListenableFuture<Void>> call() {
+                            List<ListenableFuture<Void>> futures = new ArrayList<>();
+                            futures.add(ElanL2GatewayUtils.installElanMacsInL2GatewayDevice(
+                                    logicalSwitchName, l2GatewayDevice));
+                            return futures;
+                        }}
+                    );
+            }
+
+            return futures;
+        }
+
+        private ListenableFuture<Void> createLogicalSwitch(L2GatewayDevice l2GatewayDevice, ElanInstance elanInstance,
+                Devices l2Device) {
+            final String logicalSwitchName = ElanL2GatewayUtils
+                    .getLogicalSwitchFromElan(elanInstance.getElanInstanceName());
+            String segmentationId = elanInstance.getVni().toString();
+
+            // Register for Logical switch update in opearational DS
+            final HwvtepLogicalSwitchListener hwVTEPLogicalSwitchListener = new HwvtepLogicalSwitchListener(
+                    l2GatewayDevice, logicalSwitchName, l2Device, defaultVlan);
+            hwVTEPLogicalSwitchListener.registerListener(LogicalDatastoreType.OPERATIONAL, broker);
+
+            NodeId hwvtepNodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId());
+            InstanceIdentifier<LogicalSwitches> path = HwvtepSouthboundUtils
+                    .createLogicalSwitchesInstanceIdentifier(hwvtepNodeId, new HwvtepNodeName(logicalSwitchName));
+            LogicalSwitches logicalSwitch = HwvtepSouthboundUtils.createLogicalSwitch(logicalSwitchName,
+                    elanInstance.getDescription(), segmentationId);
+
+            ListenableFuture<Void> lsCreateFuture = HwvtepUtils.addLogicalSwitch(broker, hwvtepNodeId, logicalSwitch);
+            Futures.addCallback(lsCreateFuture, new FutureCallback<Void>() {
+                @Override
+                public void onSuccess(Void noarg) {
+                    // Listener will be closed after all configuration completed
+                    // on hwvtep by
+                    // listener itself
+                    if (LOG.isTraceEnabled()) {
+                        LOG.trace("Successful in initiating logical switch {} creation", logicalSwitchName);
+                    }
+                }
+
+                @Override
+                public void onFailure(Throwable error) {
+                    LOG.error("Failed logical switch {} creation", logicalSwitchName, error);
+                    try {
+                        hwVTEPLogicalSwitchListener.close();
+                    } catch (final Exception e) {
+                        LOG.error("Error when cleaning up DataChangeListener.", e);
+                    }
+                }
+            });
+            return lsCreateFuture;
+        }
+    }
+
+    private static class DisAssociateHwvtepFromElan implements Callable<List<ListenableFuture<Void>>> {
+        DataBroker broker;
+        L2GatewayDevice l2GatewayDevice;
+        ElanInstance elanInstance;
+        Devices l2Device;
+        Integer defaultVlan;
+
+        public DisAssociateHwvtepFromElan(DataBroker broker, L2GatewayDevice l2GatewayDevice, ElanInstance elanInstance,
+                Devices l2Device, Integer defaultVlan) {
+            this.broker = broker;
+            this.l2GatewayDevice = l2GatewayDevice;
+            this.elanInstance = elanInstance;
+            this.l2Device = l2Device;
+            this.defaultVlan = defaultVlan;
+        }
+
+        @Override
+        public List<ListenableFuture<Void>> call() throws Exception {
+            List<ListenableFuture<Void>> futures = new ArrayList<>();
+
+            // Remove remote MACs and vlan mappings from physical port
+            // Once all above configurations are deleted, delete logical
+            // switch
+            NodeId hwvtepNodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId());
+            String elanName = elanInstance.getElanInstanceName();
+            futures.add(ElanL2GatewayUtils.deleteElanMacsFromL2GatewayDevice(l2GatewayDevice, elanName));
+            futures.addAll(ElanL2GatewayMulticastUtils.handleMcastForElanL2GwDeviceDelete(elanInstance,
+                    l2GatewayDevice));
+            futures.addAll(ElanL2GatewayUtils.deleteL2GatewayDeviceUcastLocalMacsFromElan(l2GatewayDevice, elanName));
+            futures.add(ElanL2GatewayUtils.deleteVlanBindingsFromL2GatewayDevice(hwvtepNodeId, l2Device, defaultVlan));
+            Thread.sleep(30000);
+            futures.add(HwvtepUtils.deleteLogicalSwitch(this.broker, hwvtepNodeId, elanName));
+
+            return futures;
+        }
+    }
+}
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitor.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitor.java
new file mode 100644 (file)
index 0000000..8f96d64
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.vpnservice.elan.statusanddiag;
+
+import java.lang.management.ManagementFactory;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ElanStatusMonitor implements ElanStatusMonitorMBean{
+
+    private String serviceStatus;
+    private static ElanStatusMonitor elanStatusMonitor = new ElanStatusMonitor();
+    private static final String JMX_ELAN_OBJ_NAME = "com.ericsson.sdncp.services.status:type=SvcElanService";
+    private static final Logger log = LoggerFactory.getLogger(ElanStatusMonitor.class);
+
+    private ElanStatusMonitor () {
+    }
+
+    public void registerMbean() {
+        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+        try {
+            ObjectName objName = new ObjectName(JMX_ELAN_OBJ_NAME);
+            mbs.registerMBean(elanStatusMonitor, objName);
+            log.info("MXBean registration SUCCESSFUL!!! {}", JMX_ELAN_OBJ_NAME);
+        } catch (InstanceAlreadyExistsException iaeEx) {
+            log.error("MXBean registration FAILED with InstanceAlreadyExistsException", iaeEx);
+        } catch (MBeanRegistrationException mbrEx) {
+            log.error("MXBean registration FAILED with MBeanRegistrationException", mbrEx);
+        } catch (NotCompliantMBeanException ncmbEx) {
+            log.error("MXBean registration FAILED with NotCompliantMBeanException", ncmbEx);
+        } catch (MalformedObjectNameException monEx) {
+            log.error("MXBean registration failed with MalformedObjectNameException", monEx);
+        }
+    }
+
+    public static ElanStatusMonitor getInstance() {
+        return elanStatusMonitor;
+    }
+
+    @Override
+    public String acquireServiceStatus() {
+        return serviceStatus;
+    }
+
+    public void reportStatus (String serviceStatus) {
+        this.serviceStatus = serviceStatus;
+    }
+}
\ No newline at end of file
diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitorMBean.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitorMBean.java
new file mode 100644 (file)
index 0000000..83a00f3
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * 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.vpnservice.elan.statusanddiag;
+
+public interface ElanStatusMonitorMBean {
+
+    public String acquireServiceStatus();
+
+}
index fb3634eb5b82d3131743eb085b5f2cc9c97dc514..51aff882abcfd5ced15831e57d04a9a506537239 100755 (executable)
@@ -31,5 +31,6 @@ public class ElanConstants {
     public static final short ELAN_FILTER_EQUALS_TABLE = 55;
     public static final BigInteger COOKIE_ELAN_FILTER_EQUALS = new BigInteger("8800000", 16);
 
-
+    public static final String L2GATEWAY_DS_JOB_NAME = "L2GW";
+    public static final String UNKNOWN_DMAC = "00:00:00:00:00:00";
 }
index 201eef26299443caf2c430c7b3edced6ab50e489..e81a2759ee653435b2e881606772ff8d095ee3ac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 - 2016  Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * 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,
@@ -7,45 +7,58 @@
  */
 package org.opendaylight.vpnservice.elan.utils;
 
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.vpnservice.elan.internal.ElanInstanceManager;
-import com.google.common.util.concurrent.CheckedFuture;
-import org.opendaylight.vpnservice.elan.internal.ElanServiceProvider;
 import org.opendaylight.vpnservice.interfacemgr.globals.InterfaceInfo;
 import org.opendaylight.vpnservice.interfacemgr.globals.InterfaceServiceUtil;
 import org.opendaylight.vpnservice.itm.globals.ITMConstants;
-import org.opendaylight.vpnservice.mdsalutil.*;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil.MdsalOp;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.NwConstants;
 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
 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.action.OutputActionCase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.field._case.SetFieldBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
+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.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteActionsCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.write.actions._case.WriteActionsBuilder;
+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.flow.types.rev131026.instruction.list.InstructionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.TunnelBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.ServiceBindings;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.ServiceTypeFlowBased;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.StypeOpenflow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.StypeOpenflowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.ServicesInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.ServicesInfoKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.services.info.BoundServices;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.services.info.BoundServicesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.services.info.BoundServicesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanDpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanForwardingTables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanInstances;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanInterfaceForwardingEntries;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanTagNameMap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMac;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan._interface.forwarding.entries.ElanInterfaceMacKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
@@ -68,7 +81,17 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.e
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.tag.name.map.ElanTagNameKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.forwarding.entries.MacEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.forwarding.entries.MacEntryKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007.IfIndexesInterfaceMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007._if.indexes._interface.map.IfIndexInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007._if.indexes._interface.map.IfIndexInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput;
@@ -76,6 +99,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpc
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.op.rev150701.TunnelList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.CreateTerminatingServiceActionsInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.CreateTerminatingServiceActionsInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetExternalTunnelInterfaceNameOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameOutput;
@@ -88,51 +114,56 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007.IfIndexesInterfaceMap;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007._if.indexes._interface.map.IfIndexInterface;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007._if.indexes._interface.map.IfIndexInterfaceKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.ServicesInfo;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.ServiceBindings;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.ServiceTypeFlowBased;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.StypeOpenflow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.StypeOpenflowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.ServicesInfoKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.services.info.BoundServices;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.services.info.BoundServicesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.servicebinding.rev151015.service.bindings.services.info.BoundServicesKey;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 
+public class ElanUtils {
 
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
+    private static final ArrayList EMPTY_LIST = new ArrayList();
 
-public class ElanUtils {
+    private static OdlInterfaceRpcService interfaceMgrRpcService;
 
-    private static ElanServiceProvider elanServiceProvider;
-    private static final Logger logger = LoggerFactory.getLogger(ElanUtils.class);
+    private static ItmRpcService itmRpcService;
 
-    public static final FutureCallback<Void> DEFAULT_CALLBACK =
-            new FutureCallback<Void>() {
-                public void onSuccess(Void result) {
-                    logger.debug("Success in Datastore operation");
-                }
+    private static IMdsalApiManager mdsalMgr;
+
+    private static DataBroker dataBroker;
 
-                public void onFailure(Throwable error) {
-                    logger.error("Error in Datastore operation", error);
-                };
-            };
+    private static final Logger logger = LoggerFactory.getLogger(ElanUtils.class);
+
+    public static final FutureCallback<Void> DEFAULT_CALLBACK = new FutureCallback<Void>() {
+        @Override
+        public void onSuccess(Void result) {
+            logger.debug("Success in Datastore operation");
+        }
 
-    public static Integer getUniqueId(IdManagerService idManager, String poolName, String idKey) {
-        AllocateIdInput getIdInput = new AllocateIdInputBuilder()
-                                           .setPoolName(poolName)
-                                           .setIdKey(idKey).build();
+        @Override
+        public void onFailure(Throwable error) {
+            logger.error("Error in Datastore operation", error);
+        }
+    };
+
+    /**
+     * Uses the IdManager to retrieve a brand new ElanTag.
+     *
+     * @param idManager
+     *            the id manager
+     * @param idKey
+     *            the id key
+     * @return the integer
+     */
+    public static Integer retrieveNewElanTag(IdManagerService idManager, String idKey) {
+
+        AllocateIdInput getIdInput = new AllocateIdInputBuilder().setPoolName(ElanConstants.ELAN_ID_POOL_NAME)
+                .setIdKey(idKey).build();
 
         try {
             Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
             RpcResult<AllocateIdOutput> rpcResult = result.get();
-            if(rpcResult.isSuccessful()) {
+            if (rpcResult.isSuccessful()) {
                 return rpcResult.getResult().getIdValue().intValue();
             } else {
                 logger.warn("RPC Call to Allocate Id returned with Errors {}", rpcResult.getErrors());
@@ -143,9 +174,26 @@ public class ElanUtils {
         return 0;
     }
 
-    public static void setElanServiceProvider(ElanServiceProvider elanService) {
-        elanServiceProvider = elanService;
+    public static DataBroker getDataBroker() {
+        return dataBroker;
+    }
+
+    public final static void setIfaceMgrRpcService(OdlInterfaceRpcService rpcService) {
+        interfaceMgrRpcService = rpcService;
+    }
+
+    public final static void setItmRpcService(ItmRpcService itmService) {
+        itmRpcService = itmService;
+    }
+
+    public final static void setDataBroker(DataBroker broker) {
+        dataBroker = broker;
+    }
+
+    public final static void setMdsalManager(IMdsalApiManager mdsalApiManager) {
+        mdsalMgr = mdsalApiManager;
     }
+
     public static void releaseId(IdManagerService idManager, String poolName, String idKey) {
         ReleaseIdInput releaseIdInput = new ReleaseIdInputBuilder().setPoolName(poolName).setIdKey(idKey).build();
         Future<RpcResult<Void>> result = idManager.releaseId(releaseIdInput);
@@ -153,12 +201,11 @@ public class ElanUtils {
 
     public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
             InstanceIdentifier<T> path) {
-
-        ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
-
+        ReadOnlyTransaction tx = (broker != null ) ? broker.newReadOnlyTransaction() : dataBroker.newReadOnlyTransaction();
         Optional<T> result = Optional.absent();
         try {
-            result = tx.read(datastoreType, path).get();
+            CheckedFuture<Optional<T>, ReadFailedException> checkedFuture = tx.read(datastoreType, path);
+            result = checkedFuture.get();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -173,15 +220,15 @@ public class ElanUtils {
         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
     }
 
+
     public static InstanceIdentifier<ElanInstance> getElanInstanceIdentifier() {
         return InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class).build();
     }
 
     //elan-instances config container
     public static ElanInstance getElanInstanceByName(String elanInstanceName) {
-        DataBroker broker = elanServiceProvider.getBroker();
         InstanceIdentifier<ElanInstance> elanIdentifierId = getElanInstanceConfigurationDataPath(elanInstanceName);
-        Optional<ElanInstance> elanInstance = read(broker, LogicalDatastoreType.CONFIGURATION, elanIdentifierId);
+        Optional<ElanInstance> elanInstance = read(dataBroker, LogicalDatastoreType.CONFIGURATION, elanIdentifierId);
         if(elanInstance.isPresent()) {
             return elanInstance.get();
         }
@@ -194,9 +241,8 @@ public class ElanUtils {
 
     //elan-interfaces Config Container
     public static ElanInterface getElanInterfaceByElanInterfaceName(String elanInterfaceName) {
-        DataBroker broker = elanServiceProvider.getBroker();
         InstanceIdentifier<ElanInterface> elanInterfaceId = getElanInterfaceConfigurationDataPathId(elanInterfaceName);
-        Optional<ElanInterface> existingElanInterface = read(broker, LogicalDatastoreType.CONFIGURATION, elanInterfaceId);
+        Optional<ElanInterface> existingElanInterface = read(dataBroker, LogicalDatastoreType.CONFIGURATION, elanInterfaceId);
         if(existingElanInterface.isPresent()) {
             return existingElanInterface.get();
         }
@@ -210,9 +256,8 @@ public class ElanUtils {
 
     //elan-state Operational container
     public static Elan getElanByName(String elanInstanceName) {
-        DataBroker broker = elanServiceProvider.getBroker();
         InstanceIdentifier<Elan> elanIdentifier = getElanInstanceOperationalDataPath(elanInstanceName);
-        Optional<Elan> elanInstance = read(broker, LogicalDatastoreType.OPERATIONAL, elanIdentifier);
+        Optional<Elan> elanInstance = read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanIdentifier);
         if(elanInstance.isPresent()) {
             return elanInstance.get();
         }
@@ -225,9 +270,8 @@ public class ElanUtils {
 
     // grouping of forwarding-entries
     public static MacEntry getInterfaceMacEntriesOperationalDataPath(String interfaceName, PhysAddress physAddress) {
-        DataBroker broker = elanServiceProvider.getBroker();
         InstanceIdentifier<MacEntry> existingMacEntryId = getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress);
-        Optional<MacEntry> existingInterfaceMacEntry = read(broker, LogicalDatastoreType.OPERATIONAL, existingMacEntryId);
+        Optional<MacEntry> existingInterfaceMacEntry = read(dataBroker, LogicalDatastoreType.OPERATIONAL, existingMacEntryId);
         if(existingInterfaceMacEntry.isPresent()) {
             return existingInterfaceMacEntry.get();
         }
@@ -235,8 +279,7 @@ public class ElanUtils {
     }
 
     public static MacEntry getInterfaceMacEntriesOperationalDataPathFromId(InstanceIdentifier identifier) {
-        DataBroker broker = elanServiceProvider.getBroker();
-        Optional<MacEntry> existingInterfaceMacEntry = read(broker, LogicalDatastoreType.OPERATIONAL, identifier);
+        Optional<MacEntry> existingInterfaceMacEntry = read(dataBroker, LogicalDatastoreType.OPERATIONAL, identifier);
         if(existingInterfaceMacEntry.isPresent()) {
             return existingInterfaceMacEntry.get();
         }
@@ -251,9 +294,8 @@ public class ElanUtils {
 
     //elan-forwarding-tables Operational container
     public static MacEntry getMacTableByElanName(String elanName, PhysAddress physAddress) {
-        DataBroker broker = elanServiceProvider.getBroker();
         InstanceIdentifier<MacEntry> macId =  getMacEntryOperationalDataPath(elanName, physAddress);
-        Optional<MacEntry> existingElanMacEntry = read(broker, LogicalDatastoreType.OPERATIONAL, macId);
+        Optional<MacEntry> existingElanMacEntry = read(dataBroker, LogicalDatastoreType.OPERATIONAL, macId);
         if(existingElanMacEntry.isPresent()) {
             return existingElanMacEntry.get();
         }
@@ -262,8 +304,7 @@ public class ElanUtils {
 
 
     public static MacEntry getMacEntryFromElanMacId(InstanceIdentifier identifier) {
-        DataBroker broker = elanServiceProvider.getBroker();
-        Optional<MacEntry> existingInterfaceMacEntry = read(broker, LogicalDatastoreType.OPERATIONAL, identifier);
+        Optional<MacEntry> existingInterfaceMacEntry = read(dataBroker, LogicalDatastoreType.OPERATIONAL, identifier);
         if(existingInterfaceMacEntry.isPresent()) {
             return existingInterfaceMacEntry.get();
         }
@@ -282,41 +323,80 @@ public class ElanUtils {
 
     //elan-interface-forwarding-entries Operational container
     public static ElanInterfaceMac getElanInterfaceMacByInterfaceName(String interfaceName) {
-        DataBroker broker = elanServiceProvider.getBroker();
         InstanceIdentifier<ElanInterfaceMac> elanInterfaceId = getElanInterfaceMacEntriesOperationalDataPath(interfaceName);
-        Optional<ElanInterfaceMac> existingElanInterface = read(broker, LogicalDatastoreType.OPERATIONAL, elanInterfaceId);
+        Optional<ElanInterfaceMac> existingElanInterface = read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanInterfaceId);
         if(existingElanInterface.isPresent()) {
             return existingElanInterface.get();
         }
         return null;
     }
 
+    /**
+     * Gets the elan interface mac addresses.
+     *
+     * @param interfaceName
+     *            the interface name
+     * @return the elan interface mac addresses
+     */
+    public static List<PhysAddress> getElanInterfaceMacAddresses(String interfaceName) {
+        List<PhysAddress> macAddresses = new ArrayList<PhysAddress>();
+        ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(interfaceName);
+        if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) {
+            List<MacEntry> macEntries = elanInterfaceMac.getMacEntry();
+            for (MacEntry macEntry : macEntries) {
+                macAddresses.add(macEntry.getMacAddress());
+            }
+        }
+        return macAddresses;
+    }
+
     public static InstanceIdentifier<ElanInterfaceMac> getElanInterfaceMacEntriesOperationalDataPath(String interfaceName) {
         return InstanceIdentifier.builder(ElanInterfaceForwardingEntries.class).child(ElanInterfaceMac.class,
                 new ElanInterfaceMacKey(interfaceName)).build();
     }
 
-    //elan-dpn-interfaces Operational Container
+    /**
+     * Returns the list of Interfaces that belong to an Elan on an specific DPN.
+     * Data retrieved from Elan's operational DS: elan-dpn-interfaces container
+     *
+     * @param elanInstanceName
+     *          name of the Elan to which the interfaces must belong to
+     * @param dpId
+     *          Id of the DPN where the interfaces are located
+     * @return
+     */
     public static DpnInterfaces getElanInterfaceInfoByElanDpn(String elanInstanceName, BigInteger dpId) {
-        DataBroker broker = elanServiceProvider.getBroker();
-        InstanceIdentifier<DpnInterfaces> elanDpnInterfacesId = getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId);
-        Optional<DpnInterfaces> elanDpnInterfaces = read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfacesId);
-        if(elanDpnInterfaces.isPresent()) {
+        InstanceIdentifier<DpnInterfaces> elanDpnInterfacesId =
+                getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId);
+        Optional<DpnInterfaces> elanDpnInterfaces = read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfacesId);
+        if ( elanDpnInterfaces.isPresent() ) {
             return elanDpnInterfaces.get();
         }
         return null;
     }
 
-    public static InstanceIdentifier<DpnInterfaces> getElanDpnInterfaceOperationalDataPath(String elanInstanceName, BigInteger dpId) {
-        return InstanceIdentifier.builder(ElanDpnInterfaces.class).child(ElanDpnInterfacesList.class,
-                new ElanDpnInterfacesListKey(elanInstanceName)).child(DpnInterfaces.class, new DpnInterfacesKey(dpId)).build();
+    /**
+     * Returns the InstanceIdentifier that points to the Interfaces of an Elan in a
+     * given DPN in the Operational DS.
+     * Data retrieved from Elans's operational DS: dpn-interfaces list
+     *
+     * @param elanInstanceName
+     *          name of the Elan to which the interfaces must belong to
+     * @param dpId
+     *          Id of the DPN where the interfaces are located
+     * @return
+     */
+    public static InstanceIdentifier<DpnInterfaces> getElanDpnInterfaceOperationalDataPath(String elanInstanceName,
+            BigInteger dpId) {
+        return InstanceIdentifier.builder(ElanDpnInterfaces.class)
+                .child(ElanDpnInterfacesList.class, new ElanDpnInterfacesListKey(elanInstanceName))
+                .child(DpnInterfaces.class, new DpnInterfacesKey(dpId)).build();
     }
 
     //elan-tag-name-map Operational Container
     public static ElanTagName getElanInfoByElanTag(long elanTag) {
-        DataBroker broker = elanServiceProvider.getBroker();
         InstanceIdentifier<ElanTagName> elanId = getElanInfoEntriesOperationalDataPath(elanTag);
-        Optional<ElanTagName> existingElanInfo = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanId);
+        Optional<ElanTagName> existingElanInfo = ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanId);
         if(existingElanInfo.isPresent()) {
             return existingElanInfo.get();
         }
@@ -329,14 +409,9 @@ public class ElanUtils {
     }
 
     // interface-index-tag operational container
-    public static IfIndexInterface getInterfaceInfoByInterfaceTag(long interfaceTag) {
-        DataBroker broker = elanServiceProvider.getBroker();
+    public static Optional<IfIndexInterface> getInterfaceInfoByInterfaceTag(long interfaceTag) {
         InstanceIdentifier<IfIndexInterface> interfaceId = getInterfaceInfoEntriesOperationalDataPath(interfaceTag);
-        Optional<IfIndexInterface> existingInterfaceInfo = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, interfaceId);
-        if(existingInterfaceInfo.isPresent()) {
-            return existingInterfaceInfo.get();
-        }
-        return null;
+        return ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, interfaceId);
     }
 
     public static InstanceIdentifier<IfIndexInterface> getInterfaceInfoEntriesOperationalDataPath(long interfaceTag) {
@@ -347,23 +422,78 @@ public class ElanUtils {
 
 
     public static InstanceIdentifier<ElanDpnInterfacesList> getElanDpnOperationDataPath(String elanInstanceName) {
-        return InstanceIdentifier.builder(ElanDpnInterfaces.class).child(ElanDpnInterfacesList.class, new ElanDpnInterfacesListKey(elanInstanceName)).build();
+        return InstanceIdentifier.builder(ElanDpnInterfaces.class).child(ElanDpnInterfacesList.class,
+                new ElanDpnInterfacesListKey(elanInstanceName)).build();
     }
 
     public static  ElanDpnInterfacesList getElanDpnInterfacesList(String elanName) {
-        DataBroker broker = elanServiceProvider.getBroker();
         InstanceIdentifier<ElanDpnInterfacesList> elanDpnInterfaceId = getElanDpnOperationDataPath(elanName);
-        Optional<ElanDpnInterfacesList> existingElanDpnInterfaces = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId);
+        Optional<ElanDpnInterfacesList> existingElanDpnInterfaces =
+                ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId);
         if(existingElanDpnInterfaces.isPresent()) {
             return existingElanDpnInterfaces.get();
         }
         return null;
     }
 
-    public static  ElanDpnInterfaces getElanDpnInterfacesList() {
-        DataBroker broker = elanServiceProvider.getBroker();
-        InstanceIdentifier<ElanDpnInterfaces> elanDpnInterfaceId = InstanceIdentifier.builder(ElanDpnInterfaces.class).build();
-        Optional<ElanDpnInterfaces> existingElanDpnInterfaces = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId);
+    /**
+     * This method is useful get all ELAN participating CSS dpIds to install
+     * program remote dmac entries and updating remote bc groups for tor
+     * integration.
+     *
+     * @param elanInstanceName
+     *            the elan instance name
+     * @return list of dpIds
+     */
+    public static List<BigInteger> getParticipatingDPNsInElanInstance(String elanInstanceName) {
+        List<BigInteger> dpIds = new ArrayList<BigInteger>();
+        InstanceIdentifier<ElanDpnInterfacesList> elanDpnInterfaceId = getElanDpnOperationDataPath(elanInstanceName);
+        Optional<ElanDpnInterfacesList> existingElanDpnInterfaces =
+                ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId);
+        if (!existingElanDpnInterfaces.isPresent()) {
+            return dpIds;
+        }
+        List<DpnInterfaces> dpnInterfaces = existingElanDpnInterfaces.get().getDpnInterfaces();
+        for (DpnInterfaces dpnInterface: dpnInterfaces) {
+            dpIds.add(dpnInterface.getDpId());
+        }
+        return dpIds;
+    }
+
+    /**
+     * To check given dpId is already present in Elan instance. This can be used
+     * to program flow entry in external tunnel table when a new access port
+     * added for first time into the ELAN instance
+     *
+     * @param dpId
+     *            the dp id
+     * @param elanInstanceName
+     *            the elan instance name
+     * @return true if dpId is already present, otherwise return false
+     */
+    public static boolean isDpnAlreadyPresentInElanInstance(BigInteger dpId, String elanInstanceName) {
+        boolean isDpIdPresent = false;
+        InstanceIdentifier<ElanDpnInterfacesList> elanDpnInterfaceId = getElanDpnOperationDataPath(elanInstanceName);
+        Optional<ElanDpnInterfacesList> existingElanDpnInterfaces =
+                ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId);
+        if (!existingElanDpnInterfaces.isPresent()) {
+            return isDpIdPresent;
+        }
+        List<DpnInterfaces> dpnInterfaces = existingElanDpnInterfaces.get().getDpnInterfaces();
+        for (DpnInterfaces dpnInterface: dpnInterfaces) {
+            if (dpnInterface.getDpId().equals(dpId)) {
+                isDpIdPresent = true;
+                break;
+            }
+        }
+        return isDpIdPresent;
+    }
+
+    public static ElanDpnInterfaces getElanDpnInterfacesList() {
+        InstanceIdentifier<ElanDpnInterfaces> elanDpnInterfaceId =
+                InstanceIdentifier.builder(ElanDpnInterfaces.class).build();
+        Optional<ElanDpnInterfaces> existingElanDpnInterfaces =
+                ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId);
         if(existingElanDpnInterfaces.isPresent()) {
             return existingElanDpnInterfaces.get();
         }
@@ -371,15 +501,32 @@ public class ElanUtils {
     }
 
     public static ElanForwardingTables getElanForwardingList() {
-        DataBroker broker = elanServiceProvider.getBroker();
-        InstanceIdentifier<ElanForwardingTables> elanForwardingTableId = InstanceIdentifier.builder(ElanForwardingTables.class).build();
-        Optional<ElanForwardingTables> existingElanForwardingList = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanForwardingTableId);
+        InstanceIdentifier<ElanForwardingTables> elanForwardingTableId =
+                InstanceIdentifier.builder(ElanForwardingTables.class).build();
+        Optional<ElanForwardingTables> existingElanForwardingList =
+                ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanForwardingTableId);
         if(existingElanForwardingList.isPresent()) {
             return existingElanForwardingList.get();
         }
         return null;
     }
 
+    /**
+     * Gets the elan mac table.
+     *
+     * @param elanName
+     *            the elan name
+     * @return the elan mac table
+     */
+    public static MacTable getElanMacTable(String elanName) {
+        InstanceIdentifier<MacTable> elanMacTableId = getElanMacTableOperationalDataPath(elanName);
+        Optional<MacTable> existingElanMacTable =
+                ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanMacTableId);
+        if (existingElanMacTable.isPresent()) {
+            return existingElanMacTable.get();
+        }
+        return null;
+    }
 
     public static long getElanLocalBCGID(long elanTag) {
         return ElanConstants.ELAN_GID_MIN + (((elanTag % ElanConstants.ELAN_GID_MIN) *2) - 1);
@@ -393,6 +540,11 @@ public class ElanUtils {
         return (BigInteger.valueOf(elanTag)).shiftLeft(24);
     }
 
+    public static BigInteger getElanMetadataLabel(long elanTag, boolean isSHFlagSet ) {
+        int shBit = (isSHFlagSet) ? 1 : 0;
+        return (BigInteger.valueOf(elanTag)).shiftLeft(24).or(BigInteger.valueOf(shBit));
+    }
+
     public static BigInteger getElanMetadataLabel(long elanTag, int lportTag) {
         return getElanMetadataLabel(elanTag).or(MetaDataUtil.getLportTagMetaData(lportTag));
     }
@@ -401,21 +553,31 @@ public class ElanUtils {
         return MetaDataUtil.METADATA_MASK_SERVICE.or(MetaDataUtil.METADATA_MASK_LPORT_TAG);
     }
 
+    /**
+     * Setting INTERNAL_TUNNEL_TABLE, SMAC, DMAC, UDMAC in this DPN and also in
+     * other DPNs.
+     *
+     * @param elanInfo
+     *            the elan info
+     * @param interfaceInfo
+     *            the interface info
+     * @param macTimeout
+     *            the mac timeout
+     * @param macAddress
+     *            the mac address
+     */
     public static void setupMacFlows(ElanInstance elanInfo, InterfaceInfo interfaceInfo, long macTimeout,
-                                     String macAddress) {
-        IMdsalApiManager mdsalApiManager = elanServiceProvider.getMdsalManager();
-        DataBroker broker = elanServiceProvider.getBroker();
-        ItmRpcService itmRpcService = elanServiceProvider.getItmRpcService();
+            String macAddress) {
         synchronized (macAddress) {
-            logger.info("Acquired lock for mac : " + macAddress + "Proceeding with install operation.");
-            setupKnownSmacFlow(elanInfo, interfaceInfo, macTimeout, macAddress, mdsalApiManager);
-            setupTermDmacFlows(interfaceInfo, mdsalApiManager);
-            setupOrigDmacFlows(elanInfo, interfaceInfo, macAddress, mdsalApiManager, broker);
+            logger.info("Acquired lock for mac : " + macAddress + "Proceeding with install operation.");
+            setupKnownSmacFlow(elanInfo, interfaceInfo, macTimeout, macAddress, mdsalMgr);
+            setupTermDmacFlows(interfaceInfo, mdsalMgr);
+            setupOrigDmacFlows(elanInfo, interfaceInfo, macAddress, mdsalMgr, dataBroker);
         }
     }
 
     public static void setupDMacFlowonRemoteDpn(ElanInstance elanInfo, InterfaceInfo interfaceInfo, BigInteger dstDpId,
-                                     String macAddress) {
+            String macAddress) {
         synchronized (macAddress) {
             logger.info("Acquired lock for mac : " + macAddress + "Proceeding with install operation.");
             setupOrigDmacFlowsonRemoteDpn(elanInfo, interfaceInfo, dstDpId, macAddress);
@@ -423,16 +585,26 @@ public class ElanUtils {
     }
 
 
+    /**
+     * Inserts a Flow in SMAC table to state that the MAC has already been learnt.
+     *
+     * @param elanInfo
+     * @param interfaceInfo
+     * @param macTimeout
+     * @param macAddress
+     * @param mdsalApiManager
+     */
     private static void setupKnownSmacFlow(ElanInstance elanInfo, InterfaceInfo interfaceInfo, long macTimeout,
-                                           String macAddress, IMdsalApiManager mdsalApiManager) {
-        FlowEntity flowEntity = getKnownSmacFlowEntity(elanInfo, interfaceInfo, macTimeout, macAddress);
+            String macAddress, IMdsalApiManager mdsalApiManager) {
+        FlowEntity flowEntity = buildKnownSmacFlow(elanInfo, interfaceInfo, macTimeout, macAddress);
         mdsalApiManager.installFlow(flowEntity);
         if (logger.isDebugEnabled()) {
-            logger.debug("Known Smac flow entry created for elan Name:{}, logical Interface port:{} and mac address:{}", elanInfo.getElanInstanceName(), elanInfo.getDescription(), macAddress);
+            logger.debug("Known Smac flow entry created for elan Name:{}, logical Interface port:{} and mac address:{}",
+                         elanInfo.getElanInstanceName(), elanInfo.getDescription(), macAddress);
         }
     }
 
-    private static FlowEntity getKnownSmacFlowEntity(ElanInstance elanInfo, InterfaceInfo interfaceInfo, long macTimeout, String macAddress) {
+    public static FlowEntity buildKnownSmacFlow(ElanInstance elanInfo, InterfaceInfo interfaceInfo, long macTimeout, String macAddress) {
         BigInteger dpId = interfaceInfo.getDpId();
         int lportTag = interfaceInfo.getInterfaceTag();
         long elanTag = elanInfo.getElanTag();
@@ -454,59 +626,99 @@ public class ElanUtils {
         return flowEntity;
     }
 
+    /**
+     * Installs a Flow in INTERNAL_TUNNEL_TABLE of the affected DPN that sends the packet through the specified
+     * interface if the tunnel_id matches the interface's lportTag
+     *
+     * @param interfaceInfo
+     * @param mdsalApiManager
+     */
     private static void setupTermDmacFlows(InterfaceInfo interfaceInfo, IMdsalApiManager mdsalApiManager) {
         BigInteger dpId = interfaceInfo.getDpId();
         int lportTag = interfaceInfo.getInterfaceTag();
-        Flow flow = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE, getFlowRef(NwConstants.INTERNAL_TUNNEL_TABLE,lportTag), 5, String.format("%s:%d","ITM Flow Entry ",lportTag), 0, 0, ITMConstants.COOKIE_ITM.add(BigInteger.valueOf(lportTag)), getTunnelIdMatchForFilterEqualsLPortTag(lportTag),
+        Flow flow = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
+                getIntTunnelTableFlowRef(NwConstants.INTERNAL_TUNNEL_TABLE, lportTag), 5,
+                String.format("%s:%d","ITM Flow Entry ",lportTag), 0, 0,
+                ITMConstants.COOKIE_ITM.add(BigInteger.valueOf(lportTag)),
+                getTunnelIdMatchForFilterEqualsLPortTag(lportTag),
                 getInstructionsInPortForOutGroup(interfaceInfo.getInterfaceName()));
         mdsalApiManager.installFlow(dpId, flow);
         if (logger.isDebugEnabled()) {
-            logger.debug("Terminating service table flow entry created on dpn:{} for logical Interface port:{}", dpId, interfaceInfo.getPortName());
+            logger.debug("Terminating service table flow entry created on dpn:{} for logical Interface port:{}",
+                         dpId, interfaceInfo.getPortName());
         }
     }
 
-    private static String getFlowRef(short tableId, int elanTag) {
+    /**
+     * Constructs the FlowName for flows installed in the Internal Tunnel Table,
+     * consisting in tableId + elanTag.
+     *
+     * @param tableId
+     * @param elanTag
+     * @return
+     */
+    public static String getIntTunnelTableFlowRef(short tableId, int elanTag) {
         return new StringBuffer().append(tableId).append(elanTag).toString();
     }
 
-    private static List<MatchInfo> getTunnelIdMatchForFilterEqualsLPortTag(int LportTag) {
+    /**
+     * Constructs the Matches that checks that the tunnel_id field contains a
+     * specific lportTag
+     *
+     * @param lportTag
+     *            lportTag that must be checked against the tunnel_id field
+     * @return
+     */
+    public static List<MatchInfo> getTunnelIdMatchForFilterEqualsLPortTag(int lportTag) {
         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
         // Matching metadata
         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {
-                BigInteger.valueOf(LportTag)}));
+                BigInteger.valueOf(lportTag)}));
         return mkMatches;
-
-
     }
 
-    public static List<Instruction> getInstructionsInPortForOutGroup(
-            String ifName) {
+    /**
+     * Constructs the Instructions that take the packet over a given interface
+     *
+     * @param ifName
+     *          Name of the interface where the packet must be sent over. It can
+     *          be a local interface or a tunnel interface (internal or external)
+     * @return
+     */
+    public static List<Instruction> getInstructionsInPortForOutGroup(String ifName) {
         List<Instruction> mkInstructions = new ArrayList<Instruction>();
-        List <Action> actionsInfos = new ArrayList <Action> ();
-        actionsInfos.addAll(ElanUtils.getEgressActionsForInterface(ifName));
-        mkInstructions.add(new InstructionBuilder().setInstruction(new ApplyActionsCaseBuilder().setApplyActions(new ApplyActionsBuilder().setAction(actionsInfos).build()).build())
-                .setKey(new InstructionKey(0)).build());
-             return mkInstructions;
-    }
-
-    public static Instruction getWriteActionInstruction(List<Action> actions) {
-        return new InstructionBuilder().setInstruction(new WriteActionsCaseBuilder().setWriteActions(new WriteActionsBuilder().setAction(actions).build()).build()).setKey(new InstructionKey(0)).build();
-    }
-
-    public static Instruction getApplyActionInstruction(List<Action> actions) {
-        return new InstructionBuilder().setInstruction(new ApplyActionsCaseBuilder().setApplyActions(new ApplyActionsBuilder().setAction(actions).build()).build()).setKey(new InstructionKey(0)).build();
-    }
-
-
-    public static List<Action> getEgressActionsForInterface(String ifName) {
+        List<Action> actions = ElanUtils.getEgressActionsForInterface(ifName, /*tunnelKey*/ null );
+
+        mkInstructions.add(MDSALUtil.buildApplyActionsInstruction(actions));
+        return mkInstructions;
+    }
+
+
+    /**
+     * Returns the list of Actions to be taken when sending the packet through
+     * an Elan interface. Note that this interface can refer to an ElanInterface
+     * where the Elan VM is attached to a DPN or an ITM tunnel interface where
+     * Elan traffic can be sent through. In this latter case, the tunnelKey is
+     * mandatory and it can hold serviceId for internal tunnels or the VNI for
+     * external tunnels.
+     *
+     * @param ifName
+     *            the if name
+     * @param tunnelKey
+     *            the tunnel key
+     * @return the egress actions for interface
+     */
+    public static List<Action> getEgressActionsForInterface(String ifName, Long tunnelKey) {
         List<Action> listAction = new ArrayList<Action>();
         try {
+            GetEgressActionsForInterfaceInput getEgressActionInput =
+                    new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(tunnelKey).build();
             Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
-                    elanServiceProvider.getInterfaceManagerRpcService().getEgressActionsForInterface(
-                            new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).build());
+                    interfaceMgrRpcService.getEgressActionsForInterface(getEgressActionInput);
             RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
-            if(!rpcResult.isSuccessful()) {
-                logger.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", ifName, rpcResult.getErrors());
+            if (!rpcResult.isSuccessful()) {
+                logger.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
+                            ifName, rpcResult.getErrors());
             } else {
                 List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> actions =
                         rpcResult.getResult().getAction();
@@ -519,32 +731,38 @@ public class ElanUtils {
     }
 
     private static void setupOrigDmacFlows(ElanInstance elanInfo, InterfaceInfo interfaceInfo, String macAddress,
-                                           IMdsalApiManager mdsalApiManager, DataBroker broker) {
+            IMdsalApiManager mdsalApiManager, DataBroker broker) {
         BigInteger dpId = interfaceInfo.getDpId();
         String ifName = interfaceInfo.getInterfaceName();
         long ifTag = interfaceInfo.getInterfaceTag();
-        long groupId = interfaceInfo.getGroupId();
         String elanInstanceName = elanInfo.getElanInstanceName();
-        List<DpnInterfaces> remoteFEs = getInvolvedDpnsInElan(elanInstanceName);
-        if(remoteFEs != null) {
-            for (DpnInterfaces remoteFE : remoteFEs) {
-                Long elanTag = elanInfo.getElanTag();
-                if (remoteFE.getDpId().equals(dpId)) {
-                    // On the local FE set up a direct output flow
+        List<DpnInterfaces> elanDpns = getInvolvedDpnsInElan(elanInstanceName);
+        if (elanDpns != null) {
+            Long elanTag = elanInfo.getElanTag();
+            for (DpnInterfaces elanDpn : elanDpns) {
+                if (elanDpn.getDpId().equals(dpId)) {
+                    // On the local DPN set up a direct output flow
                     setupLocalDmacFlow(elanTag, dpId, ifName, macAddress, elanInstanceName, mdsalApiManager, ifTag);
-                    if (logger.isDebugEnabled()) {
-                        logger.debug("Dmac flow entry created for elan Name:{}, logical port Name:{} and mac address:{} on dpn:{}", elanInstanceName, interfaceInfo.getPortName(), macAddress, dpId);
-                    }
+                    logger.debug("Dmac flow entry created for elan Name:{}, logical port Name:{} and mac address:{} on dpn:{}",
+                                 elanInstanceName, interfaceInfo.getPortName(), macAddress, dpId);
                 } else {
-                    if (isDpnPresent(remoteFE.getDpId())) {
-                        // Check for the Remote DPN present in Inventory Manager
-                        setupRemoteDmacFlow(remoteFE.getDpId(), dpId, interfaceInfo.getInterfaceTag(), elanTag, macAddress, elanInstanceName);
-                        if (logger.isDebugEnabled()) {
-                            logger.debug("Dmac flow entry created for elan Name:{}, logical port Name:{} and mac address:{} on dpn:{}", elanInstanceName, interfaceInfo.getPortName(), macAddress, remoteFE.getDpId());
-                        }
+                    // Check for the Remote DPN present in Inventory Manager
+                    if (isDpnPresent(elanDpn.getDpId())) {
+                        // For remote DPNs a flow is needed to indicate that packets of this ELAN going to this MAC
+                        // need to be forwarded through the appropiated ITM tunnel
+                        setupRemoteDmacFlow(elanDpn.getDpId(),   // srcDpn (the remote DPN in this case)
+                                            dpId,                // dstDpn (the local DPN)
+                                            interfaceInfo.getInterfaceTag(), // lportTag of the local interface
+                                            elanTag,             // identifier of the Elan
+                                            macAddress,          // MAC to be programmed in remote DPN
+                                            elanInstanceName);
+                        logger.debug("Dmac flow entry created for elan Name:{}, logical port Name:{} and mac address:{} on dpn:{}",
+                                     elanInstanceName, interfaceInfo.getPortName(), macAddress, elanDpn.getDpId());
                     }
                 }
             }
+
+            // TODO (eperefr): Make sure that the same is performed against the ElanDevices.
         }
     }
 
@@ -569,12 +787,15 @@ public class ElanUtils {
     @SuppressWarnings("unchecked")
     public static List<DpnInterfaces> getInvolvedDpnsInElan(String elanName) {
         List<DpnInterfaces> dpns = ElanInstanceManager.getElanInstanceManager().getElanDPNByName(elanName);
+        if (dpns == null) {
+            return EMPTY_LIST;
+        }
         return dpns;
     }
 
     private static void setupLocalDmacFlow(long elanTag, BigInteger dpId, String ifName, String macAddress,
                                            String displayName, IMdsalApiManager mdsalApiManager, long ifTag) {
-        Flow flowEntity = getLocalDmacFlowEntry(elanTag, dpId, ifName, macAddress, displayName, ifTag);
+        Flow flowEntity = buildLocalDmacFlowEntry(elanTag, dpId, ifName, macAddress, displayName, ifTag);
         mdsalApiManager.installFlow(dpId, flowEntity);
 
     }
@@ -587,56 +808,99 @@ public class ElanUtils {
         return new StringBuffer().append(tableId).append(elanTag).append(dpId).append(remoteDpId).append(macAddress).toString();
     }
 
-    public static Flow getLocalDmacFlowEntry(long elanTag, BigInteger dpId, String ifName, String macAddress,
-                                                   String displayName, long ifTag) {
+    private static String getKnownDynamicmacFlowRef(short elanDmacTable, BigInteger dpId, String extDeviceNodeId,
+            String dstMacAddress, long elanTag, boolean shFlag) {
+        return new StringBuffer().append(elanDmacTable).append(elanTag).append(dpId)
+                                 .append(extDeviceNodeId).append(dstMacAddress).append(shFlag)
+                                 .toString();
+    }
+
+    /**
+     * Builds the flow to be programmed in the DMAC table of the local DPN (that is, where the MAC is attached to).
+     * This flow consists in:
+     *
+     *  Match:
+     *     + elanTag in metadata
+     *     + packet goes to a MAC locally attached
+     *  Actions:
+     *     + optionally, pop-vlan + set-vlan-id
+     *     + output to ifName's portNumber
+     *
+     * @param elanTag the elan tag
+     * @param dpId the dp id
+     * @param ifName the if name
+     * @param macAddress the mac address
+     * @param displayName the display name
+     * @param ifTag the if tag
+     * @return the flow
+     */
+    public static Flow buildLocalDmacFlowEntry(long elanTag, BigInteger dpId, String ifName, String macAddress,
+                                               String displayName, long ifTag) {
         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
-        mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                ElanUtils.getElanMetadataLabel(elanTag),
-                MetaDataUtil.METADATA_MASK_SERVICE }));
+        mkMatches.add(new MatchInfo(MatchFieldType.metadata,
+                new BigInteger[] { ElanUtils.getElanMetadataLabel(elanTag), MetaDataUtil.METADATA_MASK_SERVICE }));
         mkMatches.add(new MatchInfo(MatchFieldType.eth_dst, new String[] { macAddress }));
 
         List<Instruction> mkInstructions = new ArrayList<Instruction>();
-        List <Action> actionsInfos = new ArrayList <Action> ();
-        actionsInfos.addAll(getEgressActionsForInterface(ifName));
-        mkInstructions.add(new InstructionBuilder().setInstruction(new ApplyActionsCaseBuilder().setApplyActions(new ApplyActionsBuilder().setAction(actionsInfos).build()).build())
-                .setKey(new InstructionKey(0)).build());
-        Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE, getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpId, ifTag, macAddress, elanTag),
-                20, displayName, 0, 0, ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
+        List<Action> actions = getEgressActionsForInterface(ifName, /* tunnelKey */ null);
+        mkInstructions.add(MDSALUtil.buildApplyActionsInstruction(actions));
+        Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE,
+                getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpId, ifTag, macAddress, elanTag), 20,
+                displayName, 0, 0, ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches,
+                mkInstructions);
 
         return flow;
     }
 
     public static void setupRemoteDmacFlow(BigInteger srcDpId, BigInteger destDpId, int lportTag, long elanTag, String macAddress,
                                            String displayName) {
-        IMdsalApiManager mdsalApiManager = elanServiceProvider.getMdsalManager();
-        Flow flowEntity = getRemoteDmacFlowEntry(srcDpId, destDpId, lportTag, elanTag, macAddress, displayName);
-        mdsalApiManager.installFlow(srcDpId, flowEntity);
-    }
-
-    public static Flow getRemoteDmacFlowEntry(BigInteger srcDpId, BigInteger destDpId, int lportTag, long elanTag,
-                                              String macAddress, String displayName) {
-        ItmRpcService itmRpcService = elanServiceProvider.getItmRpcService();
+        Flow flowEntity = buildRemoteDmacFlowEntry(srcDpId, destDpId, lportTag, elanTag, macAddress, displayName);
+        mdsalMgr.installFlow(srcDpId, flowEntity);
+    }
+
+    /**
+     * Builds a Flow to be programmed in a remote DPN's DMAC table.
+     * This flow consists in:
+     *  Match:
+     *    + elanTag in packet's metadata
+     *    + packet going to a MAC known to be located in another DPN
+     *  Actions:
+     *    + set_tunnel_id(lportTag)
+     *    + output ITM internal tunnel interface with the other DPN
+     *
+     * @param srcDpId
+     * @param destDpId
+     * @param lportTag
+     * @param elanTag
+     * @param macAddress
+     * @param displayName
+     * @return
+     */
+    public static Flow buildRemoteDmacFlowEntry(BigInteger srcDpId, BigInteger destDpId, int lportTag, long elanTag,
+                                                 String macAddress, String displayName) {
         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
-        mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[]{
-                ElanUtils.getElanMetadataLabel(elanTag),
-                MetaDataUtil.METADATA_MASK_SERVICE }));
+        mkMatches.add(new MatchInfo(MatchFieldType.metadata,
+                                    new BigInteger[]{ ElanUtils.getElanMetadataLabel(elanTag),
+                                                      MetaDataUtil.METADATA_MASK_SERVICE }));
         mkMatches.add(new MatchInfo(MatchFieldType.eth_dst, new String[] { macAddress }));
 
         List<Instruction> mkInstructions = new ArrayList<Instruction>();
 
-        //List of ActionInfo for the provided Source and Destination DPIDs
+        //List of Action for the provided Source and Destination DPIDs
         try {
-            List<Action> actionsInfos = getItmEgressAction(srcDpId, destDpId, lportTag);
-            Instruction instruction = new InstructionBuilder().setInstruction(new ApplyActionsCaseBuilder().setApplyActions(new ApplyActionsBuilder().setAction(actionsInfos).build()).build())
-                    .setKey(new InstructionKey(0)).build();
-            mkInstructions.add(instruction);
+            List<Action> actions = getInternalItmEgressAction(srcDpId, destDpId, lportTag);
+            mkInstructions.add(MDSALUtil.buildApplyActionsInstruction(actions));
         } catch (Exception e) {
             logger.error("Interface Not Found exception");
         }
 
 
-        Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE, getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, srcDpId, destDpId, macAddress, elanTag), 20
-                , displayName, 0, 0, ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
+        Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE,
+                getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, srcDpId, destDpId, macAddress, elanTag),
+                20,  /* prio */
+                displayName, 0,   /* idleTimeout */
+                0,   /* hardTimeout */
+                ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
 
         return flow;
 
@@ -649,71 +913,91 @@ public class ElanUtils {
         String macAddress = macEntry.getMacAddress().getValue();
         synchronized (macAddress) {
             logger.info("Acquired lock for mac : " + macAddress + "Proceeding with remove operation.");
-            deleteMacFlows(elanInfo, interfaceInfo,  macAddress, true);
+            deleteMacFlows(elanInfo, interfaceInfo,  macAddress, /* alsoDeleteSMAC */ true);
         }
     }
 
     public static void deleteMacFlows(ElanInstance elanInfo, InterfaceInfo interfaceInfo, String macAddress, boolean deleteSmac) {
         String elanInstanceName = elanInfo.getElanInstanceName();
-        String ifName = interfaceInfo.getInterfaceName();
         long ifTag = interfaceInfo.getInterfaceTag();
         List<DpnInterfaces> remoteFEs = getInvolvedDpnsInElan(elanInstanceName);
-        IMdsalApiManager mdsalApiManager = elanServiceProvider.getMdsalManager();
-        ItmRpcService itmRpcService = elanServiceProvider.getItmRpcService();
         BigInteger srcdpId = interfaceInfo.getDpId();
-        String displayName = elanInstanceName;
-        long groupId = interfaceInfo.getGroupId();
         for (DpnInterfaces dpnInterface: remoteFEs) {
             Long elanTag = elanInfo.getElanTag();
-            if (dpnInterface.getDpId().equals(srcdpId)) {
+            BigInteger dstDpId = dpnInterface.getDpId();
+            if (dstDpId.equals(srcdpId)) {
                 if(deleteSmac) {
-                    mdsalApiManager.removeFlow(getKnownSmacFlowEntity(elanInfo, interfaceInfo, 0, macAddress));
+                    mdsalMgr.removeFlow(srcdpId, MDSALUtil.buildFlow(ElanConstants.ELAN_SMAC_TABLE,
+                            getKnownDynamicmacFlowRef(ElanConstants.ELAN_SMAC_TABLE, srcdpId, ifTag, macAddress, elanTag)));
                 }
-                mdsalApiManager.removeFlow(dpnInterface.getDpId(), getLocalDmacFlowEntry(elanTag, dpnInterface.getDpId(), ifName, macAddress, displayName, ifTag));
-                RemoveTerminatingServiceActionsInput removeTerminatingServiceActionsInput = new RemoveTerminatingServiceActionsInputBuilder().setServiceId(interfaceInfo.getInterfaceTag()).setDpnId(dpnInterface.getDpId()).build();
+                mdsalMgr.removeFlow(srcdpId, MDSALUtil.buildFlow(ElanConstants.ELAN_DMAC_TABLE,
+                        getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, srcdpId, ifTag, macAddress, elanTag)));
+                RemoveTerminatingServiceActionsInput removeTerminatingServiceActionsInput = new RemoveTerminatingServiceActionsInputBuilder().setServiceId(interfaceInfo.getInterfaceTag()).setDpnId(srcdpId).build();
                 itmRpcService.removeTerminatingServiceActions(removeTerminatingServiceActionsInput);
-                  if (logger.isDebugEnabled()) {
-                    logger.debug("All the required flows deleted for elan:{}, logical Interface port:{} and mac address:{} on dpn:{}", elanInstanceName, interfaceInfo.getPortName(), macAddress, dpnInterface.getDpId());
+                if (logger.isDebugEnabled()) {
+                    logger.debug("All the required flows deleted for elan:{}, logical Interface port:{} and mac address:{} on dpn:{}", elanInstanceName, interfaceInfo.getPortName(), macAddress, srcdpId);
                 }
-            } else if (isDpnPresent(dpnInterface.getDpId())) {
-                mdsalApiManager.removeFlow(dpnInterface.getDpId(),
-                        getRemoteDmacFlowEntry(dpnInterface.getDpId(), srcdpId, interfaceInfo.getInterfaceTag(), elanTag, macAddress,
-                                displayName));
+            } else if (isDpnPresent(dstDpId)) {
+                mdsalMgr.removeFlow(dstDpId, MDSALUtil.buildFlow(ElanConstants.ELAN_DMAC_TABLE,
+                        getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dstDpId, srcdpId, macAddress, elanTag)));
                 if (logger.isDebugEnabled()) {
-                    logger.debug("Dmac flow entry deleted for elan:{}, logical interface port:{} and mac address:{} on dpn:{}", elanInstanceName, interfaceInfo.getPortName(), macAddress, dpnInterface.getDpId());
+                    logger.debug("Dmac flow entry deleted for elan:{}, logical interface port:{} and mac address:{} on dpn:{}", elanInstanceName, interfaceInfo.getPortName(), macAddress, dstDpId);
                 }
             }
         }
     }
 
-    public static void UpdateOperationalDataStore(DataBroker broker, IdManagerService idManager, ElanInstance elanInstanceAdded) {
+    /**
+     * Updates the Elan information in the Operational DS. It also updates the
+     * ElanInstance in the Config DS by setting the adquired elanTag.
+     *
+     * @param broker
+     *            the broker
+     * @param idManager
+     *            the id manager
+     * @param elanInstanceAdded
+     *            the elan instance added
+     */
+    public static void updateOperationalDataStore(DataBroker broker, IdManagerService idManager,
+            ElanInstance elanInstanceAdded) {
         String elanInstanceName = elanInstanceAdded.getElanInstanceName();
-        long elanTag = ElanUtils.getUniqueId(idManager, ElanConstants.ELAN_ID_POOL_NAME, elanInstanceName);
+        long elanTag = ElanUtils.retrieveNewElanTag(idManager, elanInstanceName);
         Elan elanInfo = new ElanBuilder().setName(elanInstanceName).setKey(new ElanKey(elanInstanceName)).build();
+
         //Add the ElanState in the elan-state operational data-store
-        MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInstanceOperationalDataPath(elanInstanceName), elanInfo);
+        MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL,
+                ElanUtils.getElanInstanceOperationalDataPath(elanInstanceName), elanInfo);
+
         //Add the ElanMacTable in the elan-mac-table operational data-store
         MacTable elanMacTable = new MacTableBuilder().setKey(new MacTableKey(elanInstanceName)).build();
-        MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanMacTableOperationalDataPath(elanInstanceName), elanMacTable);
-        ElanTagName elanTagName = new ElanTagNameBuilder().setElanTag(elanTag).setKey(new ElanTagNameKey(elanTag)).setName(elanInstanceName).build();
+        MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL,
+                ElanUtils.getElanMacTableOperationalDataPath(elanInstanceName), elanMacTable);
+
+        ElanTagName elanTagName = new ElanTagNameBuilder().setElanTag(elanTag).setKey(new ElanTagNameKey(elanTag))
+                .setName(elanInstanceName).build();
+
         //Add the ElanTag to ElanName in the elan-tag-name Operational data-store
-        MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, ElanUtils.getElanInfoEntriesOperationalDataPath(elanTag), elanTagName);
-        ElanInstance elanInstanceWithTag = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName).setDescription(elanInstanceAdded.getDescription()).setMacTimeout(elanInstanceAdded
-                .getMacTimeout() == null ? ElanConstants.DEFAULT_MAC_TIME_OUT : elanInstanceAdded.getMacTimeout()).setKey(elanInstanceAdded.getKey()).setElanTag(elanTag).build();
-        MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, getElanInstanceConfigurationDataPath(elanInstanceName), elanInstanceWithTag);
+        MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL,
+                ElanUtils.getElanInfoEntriesOperationalDataPath(elanTag), elanTagName);
+
+        // Updates the ElanInstance Config DS by setting the just adquired elanTag
+        ElanInstance elanInstanceWithTag = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName)
+                .setDescription(elanInstanceAdded.getDescription())
+                .setMacTimeout(elanInstanceAdded.getMacTimeout() == null ?  ElanConstants.DEFAULT_MAC_TIME_OUT
+                        : elanInstanceAdded.getMacTimeout())
+                .setKey(elanInstanceAdded.getKey()).setElanTag(elanTag).build();
+        MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION,
+                getElanInstanceConfigurationDataPath(elanInstanceName), elanInstanceWithTag);
     }
 
     public static boolean isDpnPresent(BigInteger dpnId) {
-        DataBroker broker = elanServiceProvider.getBroker();
-        boolean isPresent = false;
         String dpn = String.format("%s:%s", "openflow",dpnId);
         NodeId nodeId = new NodeId(dpn);
-        InstanceIdentifier<Node> node = InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(nodeId)).build();
-        Optional<Node> nodePresent = read(broker, LogicalDatastoreType.OPERATIONAL, node);
-        if(nodePresent.isPresent()) {
-            isPresent = true;
-        }
-        return isPresent;
+
+        InstanceIdentifier<Node> node = InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(nodeId))
+                .build();
+        Optional<Node> nodePresent = read(dataBroker, LogicalDatastoreType.OPERATIONAL, node);
+        return (nodePresent.isPresent());
     }
 
     public static ServicesInfo getServiceInfo(String elanInstanceName, long elanTag, String interfaceName) {
@@ -729,14 +1013,14 @@ public class ElanUtils {
     }
 
     public static <T extends DataObject> void delete(DataBroker broker, LogicalDatastoreType datastoreType,
-                                              InstanceIdentifier<T> path, FutureCallback<Void> callback) {
+            InstanceIdentifier<T> path, FutureCallback<Void> callback) {
         WriteTransaction tx = broker.newWriteOnlyTransaction();
         tx.delete(datastoreType, path);
         Futures.addCallback(tx.submit(), callback);
     }
 
     public static <T extends DataObject> void syncWrite(DataBroker broker, LogicalDatastoreType datastoreType,
-                                                        InstanceIdentifier<T> path, T data) {
+            InstanceIdentifier<T> path, T data) {
         WriteTransaction tx = broker.newWriteOnlyTransaction();
         tx.put(datastoreType, path, data, true);
         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
@@ -750,7 +1034,7 @@ public class ElanUtils {
 
 
     public static BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
-                                                 BigInteger cookie, List<Instruction> instructions) {
+            BigInteger cookie, List<Instruction> instructions) {
         StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie).setFlowPriority(flowPriority).setInstruction(instructions);
         return new BoundServicesBuilder().setKey(new BoundServicesKey(servicePriority))
                 .setServiceName(serviceName).setServicePriority(servicePriority)
@@ -762,56 +1046,128 @@ public class ElanUtils {
                 .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
     }
 
+    /**
+     * Builds the list of actions to be taken when sending the packet over a
+     * VxLan Tunnel Interface, such as setting the tunnel_id field, the vlanId
+     * if proceeds and output the packet over the right port.
+     *
+     * @param tunnelIfaceName
+     *            the tunnel iface name
+     * @param tunnelKey
+     *            the tunnel key
+     * @return the list
+     */
+    public static List<Action> buildItmEgressActions(String tunnelIfaceName, Long tunnelKey) {
+        List<Action> result = EMPTY_LIST;
+        if (tunnelIfaceName != null && !tunnelIfaceName.isEmpty()) {
+            GetEgressActionsForInterfaceInput getEgressActInput = new GetEgressActionsForInterfaceInputBuilder()
+                    .setIntfName(tunnelIfaceName).setTunnelKey(tunnelKey).build();
+
+            Future<RpcResult<GetEgressActionsForInterfaceOutput>> egressActionsOutputFuture =
+                    interfaceMgrRpcService.getEgressActionsForInterface(getEgressActInput);
+            try {
+                if (egressActionsOutputFuture.get().isSuccessful()) {
+                    GetEgressActionsForInterfaceOutput egressActionsOutput = egressActionsOutputFuture.get().getResult();
+                    result = egressActionsOutput.getAction();
+                }
+            } catch (InterruptedException | ExecutionException e) {
+                logger.error("Error in RPC call getEgressActionsForInterface {}", e);
+            }
+        }
+
+        if ( result == null || result.size() == 0 ) {
+            logger.warn("Could not build Egress actions for interface {} and tunnelId {}", tunnelIfaceName, tunnelKey);
+        }
+        return result;
+    }
 
-    public static List<Action> getItmEgressAction(BigInteger sourceDpnId,
-                                                      BigInteger destinationDpnId, int serviceTag) {
-        ItmRpcService itmManager = elanServiceProvider.getItmRpcService();
-        OdlInterfaceRpcService interfaceManagerRpcService = elanServiceProvider.getInterfaceManagerRpcService();
-        logger.debug("In getItmIngress Action source {}, destination {}, elanTag {}", sourceDpnId, destinationDpnId, serviceTag);
-        List<Action> actions = new ArrayList<>();
-        String tunnelInterfaceName;
-        GetTunnelInterfaceNameInput input = new GetTunnelInterfaceNameInputBuilder().setDestinationDpid(destinationDpnId).setSourceDpid(sourceDpnId).build();
-        Future<RpcResult<GetTunnelInterfaceNameOutput>> output = itmManager.getTunnelInterfaceName(input);
+    /**
+     * Builds the list of actions to be taken when sending the packet over an
+     * external VxLan tunnel interface, such as stamping the VNI on the VxLAN
+     * header, setting the vlanId if it proceeds and output the packet over the
+     * right port.
+     *
+     * @param srcDpnId
+     *            Dpn where the tunnelInterface is located
+     * @param torNode
+     *            NodeId of the ExternalDevice where the packet must be sent to.
+     * @param vni
+     *            Vni to be stamped on the VxLAN Header.
+     * @return the external itm egress action
+     */
+    public static List<Action> getExternalItmEgressAction(BigInteger srcDpnId, NodeId torNode, long vni ) {
+        List<Action> result = EMPTY_LIST;
+
+        GetExternalTunnelInterfaceNameInput input = new GetExternalTunnelInterfaceNameInputBuilder()
+                .setDestinationNode(torNode.getValue()).setSourceNode(srcDpnId.toString()).build();
+        Future<RpcResult<GetExternalTunnelInterfaceNameOutput>> output =
+                itmRpcService.getExternalTunnelInterfaceName(input);
         try {
-            GetTunnelInterfaceNameOutput tunnelInterfaceNameOutput = output.get().getResult();
-            tunnelInterfaceName = tunnelInterfaceNameOutput.getInterfaceName();
-            logger.debug("Received tunnelInterfaceName from getTunnelInterfaceName RPC {}", tunnelInterfaceName);
+            if (output.get().isSuccessful()) {
+                GetExternalTunnelInterfaceNameOutput tunnelInterfaceNameOutput = output.get().getResult();
+                String tunnelIfaceName = tunnelInterfaceNameOutput.getInterfaceName();
+                if ( logger.isDebugEnabled() )
+                    logger.debug("Received tunnelInterfaceName from getTunnelInterfaceName RPC {}", tunnelIfaceName);
+
+                result = buildItmEgressActions(tunnelIfaceName, vni);
+            }
+
         } catch (InterruptedException | ExecutionException e) {
             logger.error("Error in RPC call getTunnelInterfaceName {}", e);
-            return actions;
-        }
-        if (tunnelInterfaceName != null && !tunnelInterfaceName.isEmpty()) {
-            try{
-                GetEgressActionsForInterfaceInput getEgressActionInput =
-                    new GetEgressActionsForInterfaceInputBuilder().setIntfName(tunnelInterfaceName).setTunnelKey(null).build();
-                Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
-                    interfaceManagerRpcService.getEgressActionsForInterface(getEgressActionInput);
-                RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
-                if (!rpcResult.isSuccessful()) {
-                    logger.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
-                        tunnelInterfaceName, rpcResult.getErrors());
-                } else {
-                    actions = rpcResult.getResult().getAction();
-                }
-            } catch (InterruptedException | ExecutionException e) {
-                logger.error("Error in RPC call getEgressActionsForInterface {}", e);
-                return actions;
+        }
+
+        return result;
+    }
+
+    /**
+     * Builds the list of actions to be taken when sending the packet over an
+     * internal VxLan tunnel interface, such as setting the serviceTag on the
+     * VNI field of the VxLAN header, setting the vlanId if it proceeds and
+     * output the packet over the right port.
+     *
+     * @param sourceDpnId
+     *            Dpn where the tunnelInterface is located
+     * @param destinationDpnId
+     *            Dpn where the packet must be sent to. It is used here in order
+     *            to select the right tunnel interface.
+     * @param serviceTag
+     *            serviceId to be sent on the VxLAN header.
+     * @return the internal itm egress action
+     */
+    public static List<Action> getInternalItmEgressAction(BigInteger sourceDpnId, BigInteger destinationDpnId,
+            long serviceTag) {
+        List<Action> result = EMPTY_LIST;
+
+        logger.debug("In getInternalItmEgressAction Action source {}, destination {}, elanTag {}",
+                     sourceDpnId, destinationDpnId, serviceTag);
+
+        GetTunnelInterfaceNameInput input = new GetTunnelInterfaceNameInputBuilder()
+                .setDestinationDpid(destinationDpnId).setSourceDpid(sourceDpnId).build();
+        Future<RpcResult<GetTunnelInterfaceNameOutput>> output = itmRpcService.getTunnelInterfaceName(input);
+        try {
+            if (output.get().isSuccessful()) {
+                GetTunnelInterfaceNameOutput tunnelInterfaceNameOutput = output.get().getResult();
+                String tunnelIfaceName = tunnelInterfaceNameOutput.getInterfaceName();
+                logger.debug("Received tunnelInterfaceName from getTunnelInterfaceName RPC {}", tunnelIfaceName);
+
+                result = buildItmEgressActions(tunnelIfaceName, serviceTag);
             }
+        } catch (InterruptedException | ExecutionException e) {
+            logger.error("Error in RPC call getTunnelInterfaceName {}", e);
         }
-        return actions;
+
+        return result;
     }
 
     public static List<MatchInfo> getTunnelMatchesForServiceId(int elanTag) {
         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
         // Matching metadata
-        mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[]{
-                BigInteger.valueOf(elanTag)}));
+        mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[]{BigInteger.valueOf(elanTag)}));
 
         return mkMatches;
     }
 
     public static void removeTerminatingServiceAction(BigInteger destDpId, int serviceId) {
-        ItmRpcService itmRpcService = elanServiceProvider.getItmRpcService();
         RemoveTerminatingServiceActionsInput input = new RemoveTerminatingServiceActionsInputBuilder().setDpnId(destDpId).setServiceId(serviceId).build();
         Future<RpcResult<Void>> futureObject = itmRpcService.removeTerminatingServiceActions(input);
         try {
@@ -827,20 +1183,286 @@ public class ElanUtils {
     }
 
     public static void createTerminatingServiceActions(BigInteger destDpId, int serviceId, List<Action> actions) {
-        ItmRpcService itmRpcService = elanServiceProvider.getItmRpcService();
         List<Instruction> mkInstructions = new ArrayList<Instruction>();
-        mkInstructions.add(getApplyActionInstruction(actions));
+        mkInstructions.add(MDSALUtil.buildApplyActionsInstruction(actions));
         CreateTerminatingServiceActionsInput input = new CreateTerminatingServiceActionsInputBuilder().setDpnId(destDpId).setServiceId(serviceId).setInstruction(mkInstructions).build();
 
         itmRpcService.createTerminatingServiceActions(input);
     }
 
-    public static TunnelList buildInternalTunnel(DataBroker dataBroker) {
+    public static TunnelList buildInternalTunnel(DataBroker broker) {
         InstanceIdentifier<TunnelList> tunnelListInstanceIdentifier = InstanceIdentifier.builder(TunnelList.class).build();
-        Optional<TunnelList> tunnelList = read(dataBroker, LogicalDatastoreType.CONFIGURATION, tunnelListInstanceIdentifier);
-        if(tunnelList.isPresent()) {
+        Optional<TunnelList> tunnelList = read(broker, LogicalDatastoreType.CONFIGURATION, tunnelListInstanceIdentifier);
+        if (tunnelList.isPresent()) {
             return tunnelList.get();
         }
         return null;
     }
+
+    /**
+     * Installs a Flow in a DPN's DMAC table. The Flow is for a MAC that is
+     * connected remotely in another CSS and accessible through an internal
+     * tunnel. It also installs the flow for dropping the packet if it came over
+     * an ITM tunnel (that is, if the Split-Horizon flag is set)
+     *
+     * @param localDpId
+     *            Id of the DPN where the MAC Addr is accessible locally
+     * @param remoteDpId
+     *            Id of the DPN where the flow must be installed
+     * @param lportTag
+     *            lportTag of the interface where the mac is connected to.
+     * @param elanTag
+     *            Identifier of the ELAN
+     * @param macAddress
+     *            MAC to be installed in remoteDpId's DMAC table
+     * @param displayName
+     *            the display name
+     */
+    public static void installDmacFlowsToInternalRemoteMac(BigInteger localDpId, BigInteger remoteDpId, long lportTag,
+            long elanTag, String macAddress, String displayName) {
+        Flow flow = buildDmacFlowForInternalRemoteMac(localDpId, remoteDpId, lportTag, elanTag, macAddress, displayName);
+        mdsalMgr.installFlow(remoteDpId, flow);
+    }
+
+    /**
+     * Installs a Flow in the specified DPN's DMAC table. The flow is for a MAC
+     * that is connected remotely in an External Device (TOR) and that is
+     * accessible through an external tunnel. It also installs the flow for
+     * dropping the packet if it came over an ITM tunnel (that is, if the
+     * Split-Horizon flag is set)
+     *
+     * @param dpnId
+     *            Id of the DPN where the flow must be installed
+     * @param extDeviceNodeId
+     *            the ext device node id
+     * @param elanTag
+     *            the elan tag
+     * @param vni
+     *            the vni
+     * @param macAddress
+     *            the mac address
+     * @param displayName
+     *            the display name
+     */
+    public static List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMac(BigInteger dpnId,
+            String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName) {
+        List<ListenableFuture<Void>> futures = new ArrayList<>();
+        synchronized (macAddress) {
+            Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName);
+            futures.add(mdsalMgr.installFlow(dpnId, flow));
+
+            Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
+            futures.add(mdsalMgr.installFlow(dpnId, dropFlow));
+        }
+        return futures;
+    }
+
+    public static List<MatchInfo> buildMatchesForElanTagShFlagAndDstMac(long elanTag, boolean shFlag, String macAddr) {
+        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+        mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                ElanUtils.getElanMetadataLabel(elanTag, shFlag), MetaDataUtil.METADATA_MASK_SERVICE_SH_FLAG }));
+        mkMatches.add(new MatchInfo(MatchFieldType.eth_dst, new String[] { macAddr }));
+
+        return mkMatches;
+    }
+
+    /**
+     * Builds a Flow to be programmed in a DPN's DMAC table. This method must be used when the MAC is located in an
+     * External Device (TOR).
+     * The flow matches on the specified MAC and
+     *   1) sends the packet over the CSS-TOR tunnel if SHFlag is not set, or
+     *   2) drops it if SHFlag is set (what means the packet came from an external tunnel)
+     *
+     * @param dpId DPN whose DMAC table is going to be modified
+     * @param extDeviceNodeId Hwvtep node where the mac is attached to
+     * @param elanTag ElanId to which the MAC is being added to
+     * @param vni the vni
+     * @param dstMacAddress The mac address to be programmed
+     * @param displayName the display name
+     * @return the flow
+     */
+    public static Flow buildDmacFlowForExternalRemoteMac(BigInteger dpId, String extDeviceNodeId, long elanTag,
+            Long vni, String dstMacAddress, String displayName ) {
+        List<MatchInfo> mkMatches = buildMatchesForElanTagShFlagAndDstMac(elanTag, /*shFlag*/ false, dstMacAddress);
+        List<Instruction> mkInstructions = new ArrayList<Instruction>();
+        try {
+            List<Action> actions = getExternalItmEgressAction(dpId, new NodeId(extDeviceNodeId), vni);
+            mkInstructions.add( MDSALUtil.buildApplyActionsInstruction(actions) );
+        } catch (Exception e) {
+            logger.error("Could not get Egress Actions for DpId={}  externalNode={}", dpId, extDeviceNodeId );
+        }
+
+        Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE,
+                getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, dstMacAddress, elanTag,
+                        false),
+                20,  /* prio */
+                displayName, 0,   /* idleTimeout */
+                0,   /* hardTimeout */
+                ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
+
+        return flow;
+    }
+
+    /**
+     * Builds the flow that drops the packet if it came through an external tunnel, that is, if the Split-Horizon
+     * flag is set.
+     *
+     * @param dpnId DPN whose DMAC table is going to be modified
+     * @param extDeviceNodeId  Hwvtep node where the mac is attached to
+     * @param elanTag ElanId to which the MAC is being added to
+     * @param dstMacAddress The mac address to be programmed
+     * @param displayName
+     * @return
+     */
+    private static Flow buildDmacFlowDropIfPacketComingFromTunnel(BigInteger dpnId, String extDeviceNodeId,
+            Long elanTag, String dstMacAddress) {
+        List<MatchInfo> mkMatches = buildMatchesForElanTagShFlagAndDstMac(elanTag, /*shFlag*/ true, dstMacAddress);
+        List<Instruction> mkInstructions = MDSALUtil.buildInstructionsDrop();
+        String flowId = getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpnId, extDeviceNodeId, dstMacAddress,
+                elanTag, true);
+        Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE, flowId, 20,  /* prio */
+                "Drop", 0,   /* idleTimeout */
+                0,   /* hardTimeout */
+                ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
+
+        return flow;
+    }
+
+    private static String getDmacDropFlowId(Long elanTag, String dstMacAddress) {
+        return new StringBuilder(ElanConstants.ELAN_DMAC_TABLE).append(elanTag).append(dstMacAddress).append("Drop")
+                .toString();
+    }
+
+    /**
+     * Builds a Flow to be programmed in a remote DPN's DMAC table. This method must be used when the MAC is located
+     * in another CSS.
+     *
+     * This flow consists in:
+     *  Match:
+     *    + elanTag in packet's metadata
+     *    + packet going to a MAC known to be located in another DPN
+     *  Actions:
+     *    + set_tunnel_id(lportTag)
+     *    + output on ITM internal tunnel interface with the other DPN
+     *
+     * @param localDpId the local dp id
+     * @param remoteDpId the remote dp id
+     * @param lportTag the lport tag
+     * @param elanTag the elan tag
+     * @param macAddress the mac address
+     * @param displayName the display name
+     * @return the flow
+     */
+    public static Flow buildDmacFlowForInternalRemoteMac(BigInteger localDpId, BigInteger remoteDpId, long lportTag,
+            long elanTag, String macAddress, String displayName) {
+        List<MatchInfo> mkMatches = buildMatchesForElanTagShFlagAndDstMac(elanTag, /*shFlag*/ false, macAddress);
+
+        List<Instruction> mkInstructions = new ArrayList<Instruction>();
+
+        try {
+            //List of Action for the provided Source and Destination DPIDs
+            List<Action> actions = getInternalItmEgressAction(localDpId, remoteDpId, lportTag);
+            mkInstructions.add( MDSALUtil.buildApplyActionsInstruction(actions) );
+        } catch (Exception e) {
+            logger.error("Could not get Egress Actions for localDpId={}  remoteDpId={}   lportTag={}",
+                         localDpId, remoteDpId, lportTag);
+        }
+
+        Flow flow = MDSALUtil.buildFlowNew(ElanConstants.ELAN_DMAC_TABLE,
+                getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, localDpId, remoteDpId, macAddress, elanTag),
+                20,  /* prio */
+                displayName, 0,   /* idleTimeout */
+                0,   /* hardTimeout */
+                ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
+
+        return flow;
+
+    }
+
+    /**
+     * Installs or removes flows in DMAC table for MACs that are/were located in
+     * an external Elan Device.
+     *
+     * @param dpId
+     *            Id of the DPN where the DMAC table is going to be modified
+     * @param extNodeId
+     *            Id of the External Device where the MAC is located
+     * @param elanTag
+     *            Id of the ELAN
+     * @param vni
+     *            VNI of the LogicalSwitch to which the MAC belongs to, and that
+     *            is associated with the ELAN
+     * @param macAddress
+     *            the mac address
+     * @param elanInstanceName
+     *            the elan instance name
+     * @param addOrRemove
+     *            Indicates if flows must be installed or removed.
+     * @see org.opendaylight.vpnservice.mdsalutil.MDSALUtil.MdsalOp
+     */
+    public static void setupDmacFlowsToExternalRemoteMac(BigInteger dpId, String extNodeId, Long elanTag, Long vni,
+            String macAddress, String elanInstanceName, MdsalOp addOrRemove) {
+        if ( addOrRemove == MdsalOp.CREATION_OP ) {
+            ElanUtils.installDmacFlowsToExternalRemoteMac(dpId, extNodeId, elanTag, vni, macAddress, elanInstanceName);
+        } else if ( addOrRemove == MdsalOp.REMOVAL_OP ) {
+            ElanUtils.deleteDmacFlowsToExternalMac(elanTag, dpId, extNodeId, macAddress );
+        }
+    }
+
+    /**
+     * Delete dmac flows to external mac.
+     *
+     * @param elanTag
+     *            the elan tag
+     * @param dpId
+     *            the dp id
+     * @param extDeviceNodeId
+     *            the ext device node id
+     * @param macToRemove
+     *            the mac to remove
+     */
+    public static List<ListenableFuture<Void>> deleteDmacFlowsToExternalMac(long elanTag, BigInteger dpId,
+            String extDeviceNodeId, String macToRemove ) {
+        List<ListenableFuture<Void>> futures = new ArrayList<>();
+        synchronized (macToRemove) {
+            // Removing the flows that sends the packet on an external tunnel
+            String flowId = getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId,
+                    macToRemove, elanTag, false);
+            Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(ElanConstants.ELAN_DMAC_TABLE)
+                    .build();
+            futures.add(mdsalMgr.removeFlow(dpId, flowToRemove));
+
+            // And now removing the drop flow
+            flowId = getKnownDynamicmacFlowRef(ElanConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
+                    elanTag, true);
+            flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(ElanConstants.ELAN_DMAC_TABLE)
+                    .build();
+            futures.add(mdsalMgr.removeFlow(dpId, flowToRemove));
+        }
+        return futures;
+    }
+
+    /**
+     * Gets the dpid from interface.
+     *
+     * @param interfaceName
+     *            the interface name
+     * @return the dpid from interface
+     */
+    public static BigInteger getDpidFromInterface(String interfaceName) {
+        BigInteger dpId = null;
+        Future<RpcResult<GetDpidFromInterfaceOutput>> output = interfaceMgrRpcService
+                .getDpidFromInterface(new GetDpidFromInterfaceInputBuilder().setIntfName(interfaceName).build());
+        try {
+            RpcResult<GetDpidFromInterfaceOutput> rpcResult = output.get();
+            if (rpcResult.isSuccessful()) {
+                dpId = rpcResult.getResult().getDpid();
+            }
+        } catch (NullPointerException | InterruptedException | ExecutionException e) {
+            logger.error("Failed to get the DPN ID: {} for interface {}: {} ", dpId, interfaceName, e);
+        }
+        return dpId;
+    }
+
 }
+
index 4df938c94a0cb5034172ac33eadbf6c43a63f059..82b13ea413f4a65cb896e7a9393c4b35c0ed9f66 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * 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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.elanservice.impl.rev150216;
 
 import org.opendaylight.vpnservice.elan.internal.ElanServiceProvider;
@@ -30,7 +37,10 @@ public class ElanServiceImplModule extends org.opendaylight.yang.gen.v1.urn.open
         provider.setInterfaceManager(getOdlinterfaceDependency());
         provider.setInterfaceManagerRpcService(rpcregistryDependency.getRpcService(OdlInterfaceRpcService.class));
         provider.setItmRpcService(rpcregistryDependency.getRpcService(ItmRpcService.class));
+        provider.setItmManager(getItmmanagerDependency());
         provider.setIdManager(idManager);
+        provider.setEntityOwnershipService(getEntityOwnershipServiceDependency());
+        provider.setBindingNormalizedNodeSerializer(getBindingNormalizedNodeSerializerDependency());
         getBrokerDependency().registerProvider(provider);
         return provider;
     }
index 618f8c00b9b075bf2daccd56f2a8ea32f5ffbb74..3baee245c340fd831fd8f60cfe99e58a349c8ca8 100644 (file)
@@ -1,12 +1,10 @@
 /*
-* Generated file
-*
-* Generated from: yang module name: elanservice-impl yang module local name: elanservice-impl
-* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
-* Generated at: Fri Dec 04 18:32:00 IST 2015
-*
-* Do not modify this file unless it is present under src/main directory
-*/
+ * 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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.elanservice.impl.rev150216;
 public class ElanServiceImplModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.elanservice.impl.rev150216.AbstractElanServiceImplModuleFactory {
 
index 20a3ed25819a611563082d775651ec585592bbbb..a2da3ff707da75f61690ede670170008ceb26889 100644 (file)
@@ -63,5 +63,9 @@
                 <property name="interfaceManager" ref="interfaceManagerRef" />
             </action>
         </command>
+       <command>
+            <action class="org.opendaylight.vpnservice.elan.cli.l2gw.L2GwUtilsCacheCli">
+            </action>
+        </command>
     </command-bundle>
 </blueprint>
\ No newline at end of file
index 6af446e7a67274f64ea3b51d34814d85643cf247..90ff11504c73f96f630272a00f185e5b4a4a1b51 100644 (file)
@@ -6,6 +6,7 @@ module elanservice-impl {
     import config { prefix config; revision-date 2013-04-05; }
     import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;}
     import opendaylight-sal-binding-broker-impl { prefix md-sal-binding-impl; revision-date 2013-10-28;}
+    import opendaylight-entity-ownership-service { prefix eos; revision-date 2015-08-10;}
     import elanmanager-api { prefix elanmgr-api; revision-date 2015-07-07;}
     import odl-mdsalutil { prefix odl-mdsal; revision-date 2015-04-10;}
     import odl-interface {prefix odlif; revision-date 2015-03-31;}
@@ -76,6 +77,22 @@ module elanservice-impl {
                     }
                 }
             }
+            container entity-ownership-service {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity eos:entity-ownership-service;
+                    }
+                }
+            }
+            container binding-normalized-node-serializer {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity md-sal-binding:binding-normalized-node-serializer;
+                    }
+                }
+            }
         }
     }
 }
\ No newline at end of file
index 72bb09d9448b83862ad7294b396c06d64f208e20..c1dae6c417673aa25c052c77f0b3977ea11a4102 100644 (file)
@@ -109,12 +109,12 @@ public class HwvtepSouthboundUtils {
     public static InstanceIdentifier<RemoteUcastMacs> createRemoteUcastMacsInstanceIdentifier(NodeId nodeId,
             String logicalSwitchName,
             MacAddress mac) {
-        InstanceIdentifier<LogicalSwitches> logicalSwitch = createLogicalSwitchesInstanceIdentifier(nodeId, 
+        InstanceIdentifier<LogicalSwitches> logicalSwitch = createLogicalSwitchesInstanceIdentifier(nodeId,
                 new HwvtepNodeName(logicalSwitchName));
         return createInstanceIdentifier(nodeId).augmentation(HwvtepGlobalAugmentation.class)
                 .child(RemoteUcastMacs.class, new RemoteUcastMacsKey(new HwvtepLogicalSwitchRef(logicalSwitch), mac));
     }
-    
+
     /**
      * Creates the local ucast macs instance identifier.
      *
@@ -127,19 +127,29 @@ public class HwvtepSouthboundUtils {
     public static InstanceIdentifier<LocalUcastMacs> createLocalUcastMacsInstanceIdentifier(NodeId nodeId,
             String logicalSwitchName,
             MacAddress mac) {
-        InstanceIdentifier<LogicalSwitches> logicalSwitch = createLogicalSwitchesInstanceIdentifier(nodeId, 
+        InstanceIdentifier<LogicalSwitches> logicalSwitch = createLogicalSwitchesInstanceIdentifier(nodeId,
                 new HwvtepNodeName(logicalSwitchName));
         return createInstanceIdentifier(nodeId).augmentation(HwvtepGlobalAugmentation.class).child(LocalUcastMacs.class,
                 new LocalUcastMacsKey(new HwvtepLogicalSwitchRef(logicalSwitch), mac));
     }
 
+    /**
+     * Creates the remote mcast macs instance identifier.
+     *
+     * @param nodeId
+     *            the node id
+     * @param logicalSwitchName
+     *            the logical switch name
+     * @param mac
+     *            the mac
+     * @return the instance identifier
+     */
     public static InstanceIdentifier<RemoteMcastMacs> createRemoteMcastMacsInstanceIdentifier(NodeId nodeId,
-            String logicalSwitchName,
-            MacAddress mac) {
-        InstanceIdentifier<LogicalSwitches> logicalSwitch = createLogicalSwitchesInstanceIdentifier(nodeId, 
+            String logicalSwitchName, MacAddress mac) {
+        InstanceIdentifier<LogicalSwitches> logicalSwitch = createLogicalSwitchesInstanceIdentifier(nodeId,
                 new HwvtepNodeName(logicalSwitchName));
-        return createInstanceIdentifier(nodeId).augmentation(HwvtepGlobalAugmentation.class).child(RemoteMcastMacs.class,
-                new RemoteMcastMacsKey(new HwvtepLogicalSwitchRef(logicalSwitch), mac));
+        return createInstanceIdentifier(nodeId).augmentation(HwvtepGlobalAugmentation.class)
+                .child(RemoteMcastMacs.class, new RemoteMcastMacsKey(new HwvtepLogicalSwitchRef(logicalSwitch), mac));
     }
 
     /**
index f913f62ccc8385b71f845d8bd2407f87baa4d13c..64756366626aab8df3cc988559ff9d75819a8d12 100644 (file)
@@ -14,6 +14,7 @@ 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.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
@@ -222,6 +223,33 @@ public final class HwvtepUtils {
         transaction.put(LogicalDatastoreType.CONFIGURATION, iid, terminationPoint, true);
     }
 
+    /**
+     * Gets the physical locator.
+     *
+     * @param broker
+     *            the broker
+     * @param datastoreType
+     *            the datastore type
+     * @param nodeId
+     *            the node id
+     * @param phyLocatorIp
+     *            the phy locator ip
+     * @return the physical locator
+     */
+    public static HwvtepPhysicalLocatorAugmentation getPhysicalLocator(DataBroker broker,
+            LogicalDatastoreType datastoreType, NodeId nodeId, final IpAddress phyLocatorIp) {
+        HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils
+                .createHwvtepPhysicalLocatorAugmentation(String.valueOf(phyLocatorIp.getValue()));
+        InstanceIdentifier<HwvtepPhysicalLocatorAugmentation> iid = HwvtepSouthboundUtils
+                .createPhysicalLocatorInstanceIdentifier(nodeId, phyLocatorAug)
+                .augmentation(HwvtepPhysicalLocatorAugmentation.class);
+        Optional<HwvtepPhysicalLocatorAugmentation> optPhyLocator = MDSALUtil.read(broker, datastoreType, iid);
+        if (optPhyLocator.isPresent()) {
+            return optPhyLocator.get();
+        }
+        return null;
+    }
+
     /**
      * Adds the remote ucast macs into config DS.
      *
@@ -271,8 +299,9 @@ public final class HwvtepUtils {
      */
     public static void putRemoteUcastMac(final WriteTransaction transaction, final NodeId nodeId,
             RemoteUcastMacs remoteUcastMac) {
-        InstanceIdentifier<RemoteUcastMacs> iid = HwvtepSouthboundUtils.createInstanceIdentifier(nodeId).augmentation(HwvtepGlobalAugmentation.class)
-        .child(RemoteUcastMacs.class, new RemoteUcastMacsKey(remoteUcastMac.getLogicalSwitchRef(), remoteUcastMac.getMacEntryKey()));
+        InstanceIdentifier<RemoteUcastMacs> iid = HwvtepSouthboundUtils.createInstanceIdentifier(nodeId)
+                .augmentation(HwvtepGlobalAugmentation.class).child(RemoteUcastMacs.class,
+                        new RemoteUcastMacsKey(remoteUcastMac.getLogicalSwitchRef(), remoteUcastMac.getMacEntryKey()));
         transaction.put(LogicalDatastoreType.CONFIGURATION, iid, remoteUcastMac, true);
     }
 
@@ -342,8 +371,7 @@ public final class HwvtepUtils {
      *            the mac
      */
     public static void deleteRemoteUcastMac(final WriteTransaction transaction, final NodeId nodeId,
-            String logialSwitchName,
-            final MacAddress mac) {
+            String logialSwitchName, final MacAddress mac) {
         transaction.delete(LogicalDatastoreType.CONFIGURATION,
                 HwvtepSouthboundUtils.createRemoteUcastMacsInstanceIdentifier(nodeId, logialSwitchName, mac));
     }
@@ -402,6 +430,30 @@ public final class HwvtepUtils {
         transaction.put(LogicalDatastoreType.CONFIGURATION, iid, remoteMcastMac, true);
     }
 
+    /**
+     * Gets the remote mcast mac.
+     *
+     * @param broker
+     *            the broker
+     * @param datastoreType
+     *            the datastore type
+     * @param nodeId
+     *            the node id
+     * @param remoteMcastMacsKey
+     *            the remote mcast macs key
+     * @return the remote mcast mac
+     */
+    public static RemoteMcastMacs getRemoteMcastMac(DataBroker broker, LogicalDatastoreType datastoreType,
+            NodeId nodeId, RemoteMcastMacsKey remoteMcastMacsKey) {
+        final InstanceIdentifier<RemoteMcastMacs> iid = HwvtepSouthboundUtils
+                .createRemoteMcastMacsInstanceIdentifier(nodeId, remoteMcastMacsKey);
+        Optional<RemoteMcastMacs> optRemoteMcastMac = MDSALUtil.read(broker, datastoreType, iid);
+        if (optRemoteMcastMac.isPresent()) {
+            return optRemoteMcastMac.get();
+        }
+        return null;
+    }
+
     /**
      * Delete remote mcast mac from config DS.
      *
index 6e2c4da5e2eee2a1e1fe55ec2e3b022373710d38..9b218278503b90305674c4dd5d5753548ead573f 100644 (file)
@@ -11,6 +11,7 @@ package org.opendaylight.vpnservice.neutronvpn.api.l2gw;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
@@ -36,6 +37,9 @@ public class L2GatewayDevice {
     /** The ucast local macs. */
     List<LocalUcastMacs> ucastLocalMacs = Collections.synchronizedList(new ArrayList<LocalUcastMacs>());
 
+    /** the status of this device connectin */
+    AtomicBoolean connected = new AtomicBoolean(true);
+
     /**
      * VTEP device name mentioned with L2 Gateway.
      *
@@ -181,6 +185,14 @@ public class L2GatewayDevice {
         ucastLocalMacs.remove(localUcastMacs);
     }
 
+    public boolean isConnected() {
+        return connected.get();
+    }
+
+    public void setConnected(boolean connected) {
+        this.connected.set(connected);
+    }
+
     /*
      * (non-Javadoc)
      *
index fd3111aee65fe178d68489eaedb8db17115981f2..d432f45cfbc7a9eee99d1bf6e5335cd02d3bc910 100644 (file)
@@ -101,8 +101,7 @@ public class NeutronNetworkChangeListener extends AbstractDataChangeListener<Net
         String segmentationId = NeutronvpnUtils.getSegmentationIdFromNeutronNetwork(input);
         ElanInstanceBuilder elanInstanceBuilder = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName);
         if (segmentationId != null) {
-            //TODO: Uncomment below line while ELAN changes are ported
-            //elanInstanceBuilder.setVni(Long.valueOf(segmentationId));
+            elanInstanceBuilder.setVni(Long.valueOf(segmentationId));
         }
         elanInstanceBuilder.setKey(new ElanInstanceKey(elanInstanceName));
         ElanInstance elanInstance = elanInstanceBuilder.build();
index bc0aa30a9095006a83ac8929d8aba699e290399a..826955dff5a553b5a27daeadb7030ea2c64aa9c7 100644 (file)
@@ -140,6 +140,11 @@ public class NeutronPortChangeListener extends AbstractDataChangeListener<Port>
     }
 
     private void handleNeutronPortCreated(Port port) {
+        if (!NeutronvpnUtils.isPortVnicTypeNormal(port)) {
+            LOG.info("Port {} is not a NORMAL VNIC Type port; OF Port interfaces are not created",
+                    port.getUuid().getValue());
+            return;
+        }
         LOG.info("Of-port-interface creation");
         // Create of-port interface for this neutron port
         String portInterfaceName = createOfPortInterface(port);
index f9e7e3d32966bbe412e650428cb1c06c4a49e140..3acc5ce43b90c8ca480945b7110a2f34266a497d 100644 (file)
@@ -20,13 +20,17 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeVxlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.Networks;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.NetworkKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.provider.ext.rev150712.NetworkProviderExtension;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.provider.ext.rev150712.neutron.networks.network.Segments;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;
@@ -68,6 +72,7 @@ import java.util.concurrent.Future;
 public class NeutronvpnUtils {
 
     private static final Logger logger = LoggerFactory.getLogger(NeutronvpnUtils.class);
+    public static final String VNIC_TYPE_NORMAL = "normal";
 
     protected static Subnetmap getSubnetmap(DataBroker broker, Uuid subnetId) {
         InstanceIdentifier id = buildSubnetMapIdentifier(subnetId);
@@ -188,6 +193,17 @@ public class NeutronvpnUtils {
         NetworkProviderExtension providerExtension = network.getAugmentation(NetworkProviderExtension.class);
         if (providerExtension != null) {
             segmentationId = providerExtension.getSegmentationId();
+            if (segmentationId == null) {
+                List<Segments> providerSegments = providerExtension.getSegments();
+                if (providerSegments != null && providerSegments.size() > 0) {
+                    for (Segments providerSegment: providerSegments) {
+                        if (isNetworkSegmentTypeVxlan(providerSegment)) {
+                            segmentationId = providerSegment.getSegmentationId();
+                            break;
+                        }
+                    }
+                }
+            }
         }
         return segmentationId;
     }
@@ -227,6 +243,16 @@ public class NeutronvpnUtils {
         return new StringBuilder().append("tap").append(tapId).toString();
     }
 
+    protected static boolean isPortVnicTypeNormal(Port port) {
+        PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
+        if(portBinding == null || portBinding.getVnicType() == null) {
+            // By default, VNIC_TYPE is NORMAL
+            return true;
+        }
+        String vnicType = portBinding.getVnicType().trim().toLowerCase();
+        return vnicType.equals(VNIC_TYPE_NORMAL);
+    }
+
     protected static boolean lock(LockManagerService lockManager, String lockName) {
         TryLockInput input = new TryLockInputBuilder().setLockName(lockName).setTime(5L).setTimeUnit
                 (TimeUnits.Milliseconds).build();
@@ -340,4 +366,8 @@ public class NeutronvpnUtils {
         return result;
     }
 
+    static boolean isNetworkSegmentTypeVxlan(Segments providerSegment) {
+        Class<? extends NetworkTypeBase> networkType = providerSegment.getNetworkType();
+        return (networkType != null && networkType.isAssignableFrom(NetworkTypeVxlan.class));
+    }
 }