From 00790a95c5e403cb62d2cc544af55e8ab3fef03b Mon Sep 17 00:00:00 2001 From: Shashidhar R Date: Thu, 7 Apr 2016 15:37:17 +0530 Subject: [PATCH] L2 Gw connection support and Elan manager changes - 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 --- elanmanager/elanmanager-api/pom.xml | 5 + .../elanmanager/utils/ElanL2GwCacheUtils.java | 115 ++ .../elanmanager-api/src/main/yang/elan.yang | 78 +- elanmanager/elanmanager-impl/pom.xml | 46 + .../src/main/config/default-config.xml | 9 + .../elan/cli/l2gw/L2GwUtilsCacheCli.java | 100 ++ .../ElanForwardingEntriesHandler.java | 1 + .../elan/internal/ElanInstanceManager.java | 20 +- .../elan/internal/ElanInterfaceManager.java | 527 ++++++-- .../ElanInterfaceStateChangeListener.java | 6 +- .../elan/internal/ElanNodeListener.java | 4 + .../elan/internal/ElanPacketInHandler.java | 28 +- .../elan/internal/ElanServiceProvider.java | 359 +++-- .../internal/ElanSmacFlowEventListener.java | 54 +- .../l2gw/internal/ElanL2GatewayProvider.java | 98 ++ .../HwvtepLocalUcastMacListener.java | 157 +++ .../HwvtepLogicalSwitchListener.java | 250 ++++ .../l2gw/listeners/HwvtepNodeListener.java | 184 +++ .../HwvtepRemoteMcastMacListener.java | 200 +++ .../L2GatewayConnectionListener.java | 121 ++ .../utils/ElanL2GatewayMulticastUtils.java | 411 ++++++ .../elan/l2gw/utils/ElanL2GatewayUtils.java | 1012 ++++++++++++++ .../l2gw/utils/L2GatewayConnectionUtils.java | 397 ++++++ .../elan/statusanddiag/ElanStatusMonitor.java | 61 + .../statusanddiag/ElanStatusMonitorMBean.java | 14 + .../vpnservice/elan/utils/ElanConstants.java | 3 +- .../vpnservice/elan/utils/ElanUtils.java | 1168 +++++++++++++---- .../impl/rev150216/ElanServiceImplModule.java | 10 + .../ElanServiceImplModuleFactory.java | 14 +- .../OSGI-INF/blueprint/blueprint.xml | 4 + .../src/main/yang/elanservice-impl.yang | 17 + .../utils/hwvtep/HwvtepSouthboundUtils.java | 26 +- .../vpnservice/utils/hwvtep/HwvtepUtils.java | 60 +- .../neutronvpn/api/l2gw/L2GatewayDevice.java | 12 + .../NeutronNetworkChangeListener.java | 3 +- .../neutronvpn/NeutronPortChangeListener.java | 5 + .../neutronvpn/NeutronvpnUtils.java | 30 + 37 files changed, 5012 insertions(+), 597 deletions(-) create mode 100644 elanmanager/elanmanager-api/src/main/java/org/opendaylight/elanmanager/utils/ElanL2GwCacheUtils.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/cli/l2gw/L2GwUtilsCacheCli.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/internal/ElanL2GatewayProvider.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLocalUcastMacListener.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLogicalSwitchListener.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepNodeListener.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepRemoteMcastMacListener.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/L2GatewayConnectionListener.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayMulticastUtils.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayUtils.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/L2GatewayConnectionUtils.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitor.java create mode 100644 elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitorMBean.java diff --git a/elanmanager/elanmanager-api/pom.xml b/elanmanager/elanmanager-api/pom.xml index 1ff47063..99bd6d59 100644 --- a/elanmanager/elanmanager-api/pom.xml +++ b/elanmanager/elanmanager-api/pom.xml @@ -59,5 +59,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html org.opendaylight.controller config-api + + org.opendaylight.vpnservice + neutronvpn-api + ${vpnservices.version} + 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 index 00000000..4022cc23 --- /dev/null +++ b/elanmanager/elanmanager-api/src/main/java/org/opendaylight/elanmanager/utils/ElanL2GwCacheUtils.java @@ -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 EMPTY_MAP = new ConcurrentHashMap(); + 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> cachedMap = + (ConcurrentMap>) CacheUtil.getCache( + ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME); + ConcurrentMap deviceMap = cachedMap.get(elanName); + if (deviceMap == null) { + synchronized(ElanL2GwCacheUtils.class) { + deviceMap = cachedMap.get(elanName); + if (deviceMap == null) { + deviceMap = new ConcurrentHashMap(); + cachedMap.put(elanName, deviceMap); + } + } + } + deviceMap.put(l2GwDevice.getHwvtepNodeId(), l2GwDevice); + } + + public static void removeL2GatewayDeviceFromAllElanCache(String deviceName) { + ConcurrentMap> cachedMap = + (ConcurrentMap>) CacheUtil.getCache( + ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME); + for (String elanName : cachedMap.keySet()) { + ConcurrentMap deviceMap = cachedMap.get(elanName); + if (deviceMap != null) { + deviceMap.remove(deviceName); + } + } + } + + + public static L2GatewayDevice removeL2GatewayDeviceFromCache(String elanName, String deviceName) { + ConcurrentMap> cachedMap = + (ConcurrentMap>) CacheUtil.getCache( + ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME); + ConcurrentMap 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> cachedMap = + (ConcurrentMap>) CacheUtil.getCache( + ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME); + ConcurrentMap deviceMap = cachedMap.get(elanName); + if (deviceMap != null) { + return deviceMap.get(deviceName); + } else { + return null; + } + } + + public static ConcurrentMap getAllElanL2GatewayDevicesFromCache(String elanName) { + ConcurrentMap> cachedMap = (ConcurrentMap>) CacheUtil + .getCache(ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME); + ConcurrentMap result = cachedMap.get(elanName); + if (result == null) { + result = EMPTY_MAP; + } + return result; + } + + public static List getAllElanDevicesFromCache() { + List l2GwsList = new ArrayList<>(); + ConcurrentMap> cachedMap = + (ConcurrentMap>) CacheUtil.getCache( + ElanL2GwCacheUtils.L2GATEWAY_CONN_CACHE_NAME); + if (cachedMap == null || cachedMap.isEmpty()) { + return null; + } + + List l2GwDevices = new ArrayList(); + for (ConcurrentMap l2gwDevices : cachedMap.values()) + { + for (L2GatewayDevice l2gwDevice : l2gwDevices.values() ) { + l2GwDevices.add(l2gwDevice); + } + } + + return l2GwDevices; + } + +} diff --git a/elanmanager/elanmanager-api/src/main/yang/elan.yang b/elanmanager/elanmanager-api/src/main/yang/elan.yang index 5b4b1055..5ec20678 100644 --- a/elanmanager/elanmanager-api/src/main/yang/elan.yang +++ b/elanmanager/elanmanager-api/src/main/yang/elan.yang @@ -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. diff --git a/elanmanager/elanmanager-impl/pom.xml b/elanmanager/elanmanager-impl/pom.xml index 19522651..b7ea5772 100644 --- a/elanmanager/elanmanager-impl/pom.xml +++ b/elanmanager/elanmanager-impl/pom.xml @@ -20,6 +20,12 @@ and is available at http://www.eclipse.org/legal/epl-v10.html elanmanager-impl ${vpnservices.version} bundle + + + 1.6.4 + 1.10.19 + + ${project.groupId} @@ -46,10 +52,25 @@ and is available at http://www.eclipse.org/legal/epl-v10.html itm-api ${vpnservices.version} + + org.opendaylight.vpnservice + neutronvpn-api + ${vpnservices.version} + org.opendaylight.controller sal-binding-broker-impl + + org.opendaylight.ovsdb + hwvtepsouthbound-api + ${vpns.ovsdb.version} + + + org.opendaylight.neutron + model + ${neutron.version} + org.apache.karaf.shell org.apache.karaf.shell.console @@ -60,6 +81,31 @@ and is available at http://www.eclipse.org/legal/epl-v10.html interfacemgr-impl ${vpnservices.version} + + org.opendaylight.vpnservice + dhcpservice-api + ${vpnservices.version} + + + + + org.mockito + mockito-all + ${mockitoall.version} + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + diff --git a/elanmanager/elanmanager-impl/src/main/config/default-config.xml b/elanmanager/elanmanager-impl/src/main/config/default-config.xml index f4f49916..b34398e2 100644 --- a/elanmanager/elanmanager-impl/src/main/config/default-config.xml +++ b/elanmanager/elanmanager-impl/src/main/config/default-config.xml @@ -13,6 +13,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28 urn:opendaylight:params:xml:ns:yang:mdsalutil:api?module=odl-mdsalutil&revision=2015-04-10 + urn:opendaylight:params:xml:ns:yang:controller:config:distributed-entity-ownership-service?module=distributed-entity-ownership-service&revision=2015-08-10 urn:opendaylight:vpnservice:itm?module=itm&revision=2015-07-01 urn:opendaylight:vpnservice:interfacemgr?module=odl-interface&revision=2015-03-31 @@ -47,6 +48,14 @@ and is available at http://www.eclipse.org/legal/epl-v10.html bindingimpl:binding-new-notification-service binding-notification-adapter + + entity-ownership:entity-ownership-service + entity-ownership-service + + + binding:binding-normalized-node-serializer + runtime-mapping-singleton + 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 index 00000000..f9954043 --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/cli/l2gw/L2GwUtilsCacheCli.java @@ -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 devices = (ConcurrentMap) 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> cache = + (ConcurrentMap>) 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 elanDevices = ElanL2GwCacheUtils + .getAllElanL2GatewayDevicesFromCache(elanName); + print(elanName, elanDevices); + } + + private void print(String elan, + ConcurrentMap 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)); + } + } +} diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanForwardingEntriesHandler.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanForwardingEntriesHandler.java index 42af1436..9be6b79b 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanForwardingEntriesHandler.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanForwardingEntriesHandler.java @@ -107,6 +107,7 @@ public class ElanForwardingEntriesHandler extends AbstractDataChangeListener macEntryId = ElanUtils.getMacEntryOperationalDataPath(elanInfo.getElanInstanceName(), macEntry.getMacAddress()); ElanUtils.delete(broker, LogicalDatastoreType.OPERATIONAL, macEntryId); + deleteElanInterfaceForwardingTablesList(interfaceInfo.getInterfaceName(), macEntry); ElanUtils.deleteMacFlows(elanInfo, interfaceInfo, macEntry); } diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInstanceManager.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInstanceManager.java index 6eed6843..89f70711 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInstanceManager.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInstanceManager.java @@ -64,6 +64,9 @@ public class ElanInstanceManager extends AbstractDataChangeListener identifier, ElanInstance del) { - logger.trace("Remove ElanInstance - Key: {}, value: {}", identifier, del); - String elanName = del.getElanInstanceName(); + protected void remove(InstanceIdentifier 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 elanInterfaces = existingElan.getElanInterfaces(); if(elanInterfaces != null && !elanInterfaces.isEmpty()) { for (String elanInterfaceName : elanInterfaces) { InstanceIdentifier 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 identifier, ElanInstance elanInstanceAdded) { Elan elanInfo = ElanUtils.getElanByName(elanInstanceAdded.getElanInstanceName()); if(elanInfo == null) { - ElanUtils.UpdateOperationalDataStore(broker, idManager, elanInstanceAdded); + ElanUtils.updateOperationalDataStore(broker, idManager, elanInstanceAdded); } } diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceManager.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceManager.java index 0dce5c96..86a5b7e9 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceManager.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceManager.java @@ -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 implements AutoCloseable { private static ElanInterfaceManager elanInterfaceManager = new ElanInterfaceManager(); @@ -157,7 +196,9 @@ public class ElanInterfaceManager extends AbstractDataChangeListener macEntries = elanInterfaceMac.getMacEntry(); @@ -177,10 +218,10 @@ public class ElanInterfaceManager extends AbstractDataChangeListener elanInterfaceId = ElanUtils.getElanInterfaceMacEntriesOperationalDataPath(interfaceName); Optional existingElanInterface = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanInterfaceId); if(existingElanInterface.isPresent()) { - List macEntries = existingElanInterface.get().getMacEntry(); - if(macEntries != null && !macEntries.isEmpty()) { + List macAddresses = new ArrayList(); + List existingMacEntries = existingElanInterface.get().getMacEntry(); + List 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 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 dpnInterfaceLists = elanDpnInterfacesList.getDpnInterfaces(); + for(DpnInterfaces dpnInterfaces : dpnInterfaceLists){ + if(dpnInterfaces.getDpId().equals(interfaceInfo.getDpId())) { + continue; + } + List remoteElanInterfaces = dpnInterfaces.getInterfaces(); + for(String remoteIf : remoteElanInterfaces) { + ElanInterfaceMac elanIfMac = ElanUtils.getElanInterfaceMacByInterfaceName(remoteIf); + InterfaceInfo remoteInterface = interfaceManager.getInterfaceInfo(remoteIf); + if(elanIfMac == null) { + continue; + } + List 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 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 elanDpnInterfaces = ElanUtils.getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId); Optional 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 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 macId = getMacEntryOperationalDataPath(elanInstanceName, physAddress); Optional existingMacEntry = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, macId); @@ -369,28 +481,18 @@ public class ElanInterfaceManager extends AbstractDataChangeListener> readFePortsDbForElan(String elanName) { - ElanDpnInterfacesList elanDpnInterfacesList = ElanUtils.getElanDpnInterfacesList(elanName); - HashMap> fePortsDb = Maps.newHashMap(); - if (elanDpnInterfacesList == null) { - return fePortsDb; - } - List 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 listAction = ElanUtils.getItmEgressAction(dpnId, dpnInterface.getDpId(), (int) elanTag); + List 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 elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId, bucketId); + listBuckets.addAll(elanL2GwDevicesBuckets); } return listBuckets; } @@ -495,7 +599,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener listActionInfo = ElanUtils.getItmEgressAction(dpnId, dpnInterface.getDpId(), (int) elanTag); + List 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 elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId, bucketId); + listBucketInfo.addAll(elanL2GwDevicesBuckets); } return listBucketInfo; } @@ -536,7 +643,7 @@ public class ElanInterfaceManager extends AbstractDataChangeListener remoteListActionInfo = ElanUtils.getItmEgressAction(dpnInterface.getDpId(), otherFes.getDpId(), (int) elanTag); + List 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 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 elanDpns = ElanUtils.getInvolvedDpnsInElan(elanInfo.getElanInstanceName()); if(elanDpns != null) { for(DpnInterfaces dpnInterface : elanDpns) { int bucketId = 0; List remoteListBucket = new ArrayList(); 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 remoteListActionInfo = ElanUtils.getItmEgressAction(interfaceInfo.getDpId(), dstDpId, (int) elanTag); + List 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 remoteListActionInfo = new ArrayList(); + 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 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 buildMatchesForVni(Long vni) { + List mkMatches = new ArrayList(); + MatchInfo match = new MatchInfo(MatchFieldType.tunnel_id, + new BigInteger[]{BigInteger.valueOf(vni)} ); + mkMatches.add(match); + return mkMatches; + } + private List getInstructionsForOutGroup( long groupId) { List mkInstructions = new ArrayList(); List actions = new ArrayList (); 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 getMatchesForElanTag(long elanTag, boolean isSHFlagSet) { + List mkMatches = new ArrayList(); + // 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 getInstructionsExtTunnelTable(Long elanTag) { + List mkInstructions = new ArrayList(); + 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 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 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 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 mkMatches = new ArrayList(); - // Matching metadata - mkMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { - ElanUtils.getElanMetadataLabel(elanTag), - MetaDataUtil.METADATA_MASK_SERVICE })); - - List mkInstructions = new ArrayList(); - List actionsInfos = new ArrayList (); - 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 getInterfacePortActions(InterfaceInfo interfaceInfo) { List listAction = new ArrayList(); 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 createElanInterfacesList(String elanInstanceName, String interfaceName, BigInteger dpId) { List interfaceNames = new ArrayList(); interfaceNames.add(interfaceName); @@ -931,6 +1141,9 @@ public class ElanInterfaceManager extends AbstractDataChangeListener macAddresses = ElanUtils + .getElanInterfaceMacAddresses(interfaceInfo.getInterfaceName()); + if (macAddresses != null && !macAddresses.isEmpty()) { + ElanL2GatewayUtils.removeMacsFromElanExternalDevices(elanInstance, macAddresses); + } + } } } @@ -1072,16 +1294,97 @@ public class ElanInterfaceManager extends AbstractDataChangeListener dpns = ElanUtils.getInvolvedDpnsInElan(elanInfo + .getElanInstanceName()); + if (dpns == null) { + return; + } + for (DpnInterfaces dpn : dpns) { + bucketId = 0; + List listBucket = new ArrayList(); + 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 getInstructionsDrop() { - List mkInstructions = new ArrayList(); - List actionsInfos = new ArrayList (); - actionsInfos.add(new ActionInfo(ActionType.drop_action, new String[]{}).buildAction()); - mkInstructions.add(ElanUtils.getWriteActionInstruction(actionsInfos)); - return mkInstructions; + private int getLocalBcGroupBuckets(DpnInterfaces dpn, + List 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 listBucket, int bucketId) { + int elanTag = elanInfo.getElanTag().intValue(); + ElanDpnInterfacesList elanDpns = ElanUtils + .getElanDpnInterfacesList(elanInfo.getElanInstanceName()); + if (elanDpns != null) { + List dpnInterfaceses = elanDpns.getDpnInterfaces(); + for (DpnInterfaces dpnInterface : dpnInterfaceses) { + if (ElanUtils.isDpnPresent(dpnInterface.getDpId()) + && dpnInterface.getDpId() != dpnId + && dpnInterface.getInterfaces() != null + && !dpnInterface.getInterfaces().isEmpty()) { + try { + List 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 elanL2GwDevicesBuckets = getRemoteBCGroupBucketsOfElanL2GwDevices(elanInfo, dpnId, bucketId); + listBucket.addAll(elanL2GwDevicesBuckets); + } + + public static List getRemoteBCGroupBucketsOfElanL2GwDevices(ElanInstance elanInfo, BigInteger dpnId, + int bucketId) { + List listBucketInfo = new ArrayList(); + ConcurrentMap map = ElanL2GwCacheUtils + .getAllElanL2GatewayDevicesFromCache(elanInfo.getElanInstanceName()); + for (L2GatewayDevice device : map.values()) { + String interfaceName = ElanL2GatewayUtils.getExternalTunnelInterfaceName(String.valueOf(dpnId), + device.getHwvtepNodeId()); + if (interfaceName == null) { + continue; + } + List listActionInfo = ElanUtils.buildItmEgressActions(interfaceName, elanInfo.getVni()); + listBucketInfo.add(MDSALUtil.buildBucket(listActionInfo, MDSALUtil.GROUP_WEIGHT, bucketId, + MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP)); + bucketId++; + } + return listBucketInfo; } } + + diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceStateChangeListener.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceStateChangeListener.java index 256e8b26..470f6237 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceStateChangeListener.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanInterfaceStateChangeListener.java @@ -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 internalTunnels = tunnelList.getInternalTunnel(); for (InternalTunnel tunnel : internalTunnels) { - if (internalTunnel.getTunnelInterfaceName().equalsIgnoreCase(interfaceName)) { + if (tunnel.getTunnelInterfaceName().equalsIgnoreCase(interfaceName)) { internalTunnel = tunnel; break; } diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanNodeListener.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanNodeListener.java index 05a808ce..679d2c9a 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanNodeListener.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanNodeListener.java @@ -71,6 +71,10 @@ public class ElanNodeListener extends AbstractDataChangeListener { protected void add(InstanceIdentifier 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); } diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanPacketInHandler.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanPacketInHandler.java index 04de7e3a..dbf83ddc 100755 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanPacketInHandler.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanPacketInHandler.java @@ -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 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())); } } diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanServiceProvider.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanServiceProvider.java index e1fafb55..af817b85 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanServiceProvider.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanServiceProvider.java @@ -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> result = idManager.createIdPool(createPool); - if ((result != null) && (result.get().isSuccessful())) { - logger.debug("ELAN Id Pool is created successfully"); + Future> 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 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 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 updatedStaticMacAddresses, String newDescription) { + public void updateElanInterface(String elanInstanceName, String interfaceName, + List updatedStaticMacAddresses, String newDescription) { ElanInterface existingElanInterface = ElanUtils.getElanInterfaceByElanInterfaceName(interfaceName); if (existingElanInterface == null) { return; @@ -249,18 +345,22 @@ public class ElanServiceProvider implements BindingAwareProvider, IElanService, List existingMacAddress = existingElanInterface.getStaticMacEntries(); List updatedMacAddresses = getPhysAddress(updatedStaticMacAddresses); List 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 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 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 getElanMacTable(String elanInstanceName) { Elan elanInfo = ElanUtils.getElanByName(elanInstanceName); List macAddress = new ArrayList<>(); - if(elanInfo == null) { + if (elanInfo == null) { return macAddress; } - List elanInterfaces = elanInfo.getElanInterfaces(); - if(elanInterfaces != null && elanInterfaces.size() > 0) { - for(String elanInterface : elanInterfaces) { + List 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 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 macEntries = elanInterfaceMac.getMacEntry(); - for(MacEntry macEntry : macEntries) { + List 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 getPhysAddress(List macAddress) { List physAddresses = new ArrayList<>(); - for(String mac : macAddress) { + for (String mac : macAddress) { physAddresses.add(new PhysAddress(mac)); } return physAddresses; } - - public List getUpdatedPhyAddress(List originalAddresses, List updatePhyAddresses) { - if(updatePhyAddresses != null && !updatePhyAddresses.isEmpty()) { + public List getUpdatedPhyAddress(List originalAddresses, + List updatePhyAddresses) { + if (updatePhyAddresses != null && !updatePhyAddresses.isEmpty()) { List 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 getElanInstances() { List elanList = new ArrayList(); - InstanceIdentifier elanInstancesIdentifier = InstanceIdentifier.builder(ElanInstances.class).build(); - Optional elansOptional = ElanUtils.read(broker, LogicalDatastoreType.CONFIGURATION, elanInstancesIdentifier); - if(elansOptional.isPresent()) { + InstanceIdentifier elanInstancesIdentifier = InstanceIdentifier.builder(ElanInstances.class) + .build(); + Optional 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 getElanInterfaces(String elanInstanceName) { List elanInterfaces = new ArrayList<>(); - InstanceIdentifier elanInterfacesIdentifier = InstanceIdentifier.builder(ElanInterfaces.class).build(); - Optional elanInterfacesOptional = ElanUtils.read(broker, LogicalDatastoreType.CONFIGURATION, elanInterfacesIdentifier); - if(!elanInterfacesOptional.isPresent()) { - return elanInterfaces; + InstanceIdentifier elanInterfacesIdentifier = InstanceIdentifier.builder(ElanInterfaces.class) + .build(); + Optional elanInterfacesOptional = ElanUtils.read(broker, LogicalDatastoreType.CONFIGURATION, + elanInterfacesIdentifier); + if (!elanInterfacesOptional.isPresent()) { + return elanInterfaces; } List 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()); } } diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanSmacFlowEventListener.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanSmacFlowEventListener.java index 9ea543e3..d8ff705d 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanSmacFlowEventListener.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/internal/ElanSmacFlowEventListener.java @@ -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 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 macEntryId = ElanUtils.getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress); - ElanUtils.delete(broker, LogicalDatastoreType.OPERATIONAL, macEntryId); + InstanceIdentifier macEntryIdForElanInterface = ElanUtils.getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress); + InstanceIdentifier macEntryIdForElanInstance = ElanUtils.getMacEntryOperationalDataPath(elanTagInfo.getName(), physAddress); + WriteTransaction tx = broker.newWriteOnlyTransaction(); + tx.delete(LogicalDatastoreType.OPERATIONAL, macEntryIdForElanInterface); + tx.delete(LogicalDatastoreType.OPERATIONAL, macEntryIdForElanInstance); + ListenableFuture writeResult = tx.submit(); + + //WRITE Callback + Futures.addCallback(writeResult, new FutureCallback() { + @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 index 00000000..76bdb6e8 --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/internal/ElanL2GatewayProvider.java @@ -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 index 00000000..c2558fe5 --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLocalUcastMacListener.java @@ -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 implements AutoCloseable { + + private DataBroker broker; + private EntityOwnershipService entityOwnershipService; + private BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer; + private ListenerRegistration 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 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 identifier, LocalUcastMacs original, + LocalUcastMacs update) { + // TODO (eperefr) what can change here? + + } + + @Override + protected void add(InstanceIdentifier 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 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 index 00000000..9edbd862 --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepLogicalSwitchListener.java @@ -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 { + + /** 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 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 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 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 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>> { + /** 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> call() throws Exception { + try { + List> 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>>() { + + @Override + public List> call() { + List> 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 index 00000000..3891246b --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepNodeListener.java @@ -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 { + 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 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 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 key, Node nodeBefore, Node nodeAfter) { + LOG.debug("Received Node Update Event: {}, {}, {}", key, nodeBefore, nodeAfter); + } + + @Override + protected void add(InstanceIdentifier 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 = 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 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 getAssociatedL2GwConnections(DataBroker broker, List l2GatewayIds) { + List l2GwConnections = null; + List allL2GwConns = getAllL2gatewayConnections(broker); + if (allL2GwConns != null) { + l2GwConnections = new ArrayList(); + for (Uuid l2GatewayId : l2GatewayIds) { + for (L2gatewayConnection l2GwConn : allL2GwConns) { + if (l2GwConn.getL2gatewayId().equals(l2GatewayId)) { + l2GwConnections.add(l2GwConn); + } + } + } + } + return l2GwConnections; + } + + protected List getAllL2gatewayConnections(DataBroker broker) { + InstanceIdentifier inst = InstanceIdentifier.create(Neutron.class) + .child(L2gatewayConnections.class); + Optional 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 index 00000000..52046271 --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/HwvtepRemoteMcastMacListener.java @@ -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 { + + /** 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>> 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>> 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 path) throws Exception { + Optional 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 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 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 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 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 index 00000000..305bb1c0 --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/listeners/L2GatewayConnectionListener.java @@ -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 implements AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(L2GatewayConnectionListener.class); + + private ListenerRegistration 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 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 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 identifier, L2gatewayConnection original, + L2gatewayConnection update) { + if (LOG.isTraceEnabled()) { + LOG.trace("Updating L2gatewayConnection : key: " + identifier + ", original value=" + original + + ", update value=" + update); + } + } + + @Override + protected InstanceIdentifier 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 index 00000000..498530ce --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayMulticastUtils.java @@ -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 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 updateRemoteMcastMacOnElanL2GwDevices(String elanName) { + SettableFuture future = SettableFuture.create(); + future.set(null); + try { + ConcurrentMap mapL2gwDevices = ElanL2GwCacheUtils + .getAllElanL2GatewayDevicesFromCache(elanName); + if (mapL2gwDevices == null || mapL2gwDevices.isEmpty()) { + LOG.trace("No L2GatewayDevices to configure RemoteMcastMac for elan {}", elanName); + return future; + } + List dpns = ElanUtils.getInvolvedDpnsInElan(elanName); + + // TODO revisit + L2GatewayDevice firstDevice = mapL2gwDevices.values().iterator().next(); + List dpnsTepIps = getAllTepIpsOfDpns(firstDevice, dpns); + List 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 updateMcastMacs(String elanName, L2GatewayDevice device, + boolean updateThisDevice) { + + SettableFuture ft = SettableFuture.create(); + ft.set(null); + + ElanInstance elanInstance = elanInstanceManager.getElanInstanceByName(elanName); + elanInterfaceManager.updateElanBroadcastGroup(elanInstance); + + List dpns = ElanUtils.getInvolvedDpnsInElan(elanName); + + ConcurrentMap devices = ElanL2GwCacheUtils + .getAllElanL2GatewayDevicesFromCache(elanName); + + List dpnsTepIps = getAllTepIpsOfDpns(device, dpns); + List 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 dpnsTepIps, List l2GwDevicesTepIps) { + NodeId nodeId = new NodeId(device.getHwvtepNodeId()); + String logicalSwitchName = ElanL2GatewayUtils.getLogicalSwitchFromElan(elanName); + + ArrayList 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 tepIps) { + List 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 getAllTepIpsOfDpns(L2GatewayDevice l2GwDevice, List dpns) { + List 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 getAllTepIpsOfL2GwDevices(ConcurrentMap devices) { + List 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> handleMcastForElanL2GwDeviceDelete(ElanInstance elanInstance, + L2GatewayDevice l2GatewayDevice) { + ListenableFuture updateMcastMacsFuture = updateMcastMacs(elanInstance.getElanInstanceName(), + l2GatewayDevice, false/* updateThisDevice */); + ListenableFuture 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 deleteRemoteMcastMac(NodeId nodeId, String logicalSwitchName) { + InstanceIdentifier 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 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 instanceIdentifier = InstanceIdentifier + .builder(DesignatedSwitchesForExternalTunnels.class) + .child(DesignatedSwitchForTunnel.class, new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp)) + .build(); + Optional 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 index 00000000..e695055f --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/ElanL2GatewayUtils.java @@ -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 macAddresses) { + String logicalSwitchName = getElanFromLogicalSwitch(elanInstance.getElanInstanceName()); + ConcurrentMap 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 installMacsInExternalDeviceAsRemoteUcastMacs(String deviceNodeId, + List macAddresses, String logicalSwitchName, IpAddress remoteVtepIp) { + NodeId nodeId = new NodeId(deviceNodeId); + HwvtepPhysicalLocatorAugmentation phyLocatorAug = HwvtepSouthboundUtils + .createHwvtepPhysicalLocatorAugmentation(String.valueOf(remoteVtepIp.getValue())); + List macs = new ArrayList(); + 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 installMacsInExternalDeviceAsRemoteUcastMacs(String elanName, + Set lstElanInterfaceNames, BigInteger dpnId, NodeId externalNodeId) { + SettableFuture 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 macAddresses) { + ConcurrentMap 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 removeRemoteUcastMacsFromExternalDevice(String deviceNodeId, + String logicalSwitchName, List macAddresses) { + NodeId nodeId = new NodeId(deviceNodeId); + + // TODO (eperefr) + List lstMac = Lists.transform(macAddresses, new Function() { + @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 lsOpc = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, + (InstanceIdentifier) 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 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 elanDpns = ElanUtils.getInvolvedDpnsInElan(elanInstanceName); + for (DpnInterfaces elanDpn : elanDpns) { + final BigInteger dpnId = elanDpn.getDpId(); + final String nodeId = getNodeIdFromDpnId(dpnId); + + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + entityOwnershipService, MDSALUtil.NODE_PREFIX, nodeId); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @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>>() { + @Override + public List> 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 macList = new ArrayList(); + macList.add(new PhysAddress(macToBeAdded)); + + ConcurrentMap elanL2GwDevices = + ElanL2GwCacheUtils.getAllElanL2GatewayDevicesFromCache(elanInstanceName); + for (L2GatewayDevice otherDevice : elanL2GwDevices.values()) { + if (!otherDevice.getHwvtepNodeId().equals(extDeviceNodeId) && !areMLAGDevices(extL2GwDevice, otherDevice)) { + final String hwvtepId = otherDevice.getHwvtepNodeId(); + InstanceIdentifier iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId)); + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE, + bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid)); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @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>>() { + @Override + public List> call() throws Exception { + final String logicalSwitchName = getLogicalSwitchFromElan(elanInstanceName); + ListenableFuture installFuture = installMacsInExternalDeviceAsRemoteUcastMacs( + hwvtepId, macList, logicalSwitchName, extL2GwDeviceTepIp); + + Futures.addCallback(installFuture, new FutureCallback() { + @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 elanDpns = ElanUtils.getInvolvedDpnsInElan(elanInstanceName); + for (DpnInterfaces elanDpn : elanDpns) { + final BigInteger dpnId = elanDpn.getDpId(); + final String nodeId = getNodeIdFromDpnId(dpnId); + + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + entityOwnershipService, MDSALUtil.NODE_PREFIX, nodeId); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @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>>() { + @Override + public List> 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 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 iid = HwvtepSouthboundUtils.createInstanceIdentifier(hwvtepNodeId); + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE, + bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid)); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @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>>() { + @Override + public List> call() throws Exception { + final String logicalSwitchName = getLogicalSwitchFromElan(elanInstanceName); + ListenableFuture uninstallFuture = HwvtepUtils.deleteRemoteUcastMac(broker, + hwvtepNodeId, logicalSwitchName, macToBeRemoved.getMacEntryKey()); + + Futures.addCallback(uninstallFuture, new FutureCallback() { + @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.
+ * 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 deleteElanMacsFromL2GatewayDevice(L2GatewayDevice l2GatewayDevice, + String elanName) { + List elanMacTableEntries = getElanMacTableEntries(elanName); + List elanL2GatewayDevicesLocalMacs = getElanL2GatewayDevicesLocalMacs(l2GatewayDevice, elanName); + + List 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 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 lstMacs = Lists.transform(macTable.getMacEntry(), new Function() { + @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 getElanL2GatewayDevicesLocalMacs(L2GatewayDevice l2GwDeviceToBeExcluded, + String elanName) { + List lstL2GatewayDeviceMacs = new ArrayList<>(); + + ConcurrentMap elanL2GwDevicesFromCache = ElanL2GwCacheUtils + .getAllElanL2GatewayDevicesFromCache(elanName); + if (elanL2GwDevicesFromCache != null) { + for (L2GatewayDevice otherDevice : elanL2GwDevicesFromCache.values()) { + if (!otherDevice.getHwvtepNodeId().equals(l2GwDeviceToBeExcluded.getHwvtepNodeId())) { + List lstUcastLocalMacs = otherDevice.getUcastLocalMacs(); + if (lstUcastLocalMacs != null) { + List l2GwDeviceMacs = Lists.transform(lstUcastLocalMacs, + new Function() { + @Override + public MacAddress apply(LocalUcastMacs localUcastMac) { + return (localUcastMac != null) ? localUcastMac.getMacEntryKey() : null; + } + }); + lstL2GatewayDeviceMacs.addAll(l2GwDeviceMacs); + } + } + } + } + return lstL2GatewayDeviceMacs; + } + + /** + * Install ELAN macs in L2 Gateway device.
+ * 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 installElanMacsInL2GatewayDevice(String elanName, + L2GatewayDevice l2GatewayDevice) { + String logicalSwitchName = getLogicalSwitchFromElan(elanName); + NodeId hwVtepNodeId = new NodeId(l2GatewayDevice.getHwvtepNodeId()); + + List lstL2GatewayDevicesMacs = getL2GatewayDevicesUcastLocalMacsAsRemoteUcastMacs(elanName, + l2GatewayDevice, hwVtepNodeId, logicalSwitchName); + List lstElanMacTableEntries = getElanMacTableEntriesAsRemoteUcastMacs(elanName, + l2GatewayDevice, hwVtepNodeId, logicalSwitchName); + + List lstRemoteUcastMacs = new ArrayList<>(lstL2GatewayDevicesMacs); + lstRemoteUcastMacs.addAll(lstElanMacTableEntries); + + ListenableFuture 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 getL2GatewayDevicesUcastLocalMacsAsRemoteUcastMacs(String elanName, + L2GatewayDevice l2GatewayDeviceToBeConfigured, NodeId hwVtepNodeId, String logicalSwitchName) { + List lstRemoteUcastMacs = new ArrayList(); + ConcurrentMap elanL2GwDevicesFromCache = ElanL2GwCacheUtils + .getAllElanL2GatewayDevicesFromCache(elanName); + + if (elanL2GwDevicesFromCache != null) { + for (L2GatewayDevice otherDevice : elanL2GwDevicesFromCache.values()) { + if (l2GatewayDeviceToBeConfigured.getHwvtepNodeId().equals(otherDevice.getHwvtepNodeId())) { + continue; + } + if (!areMLAGDevices(l2GatewayDeviceToBeConfigured, otherDevice)) { + List 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.
+ * 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 getElanMacTableEntriesAsRemoteUcastMacs(String elanName, + L2GatewayDevice l2GatewayDeviceToBeConfigured, NodeId hwVtepNodeId, String logicalSwitchName) { + List lstRemoteUcastMacs = new ArrayList(); + + 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> output = itmRpcService + .getExternalTunnelInterfaceName(new GetExternalTunnelInterfaceNameInputBuilder() + .setSourceNode(sourceNode).setDestinationNode(dstNode).build()); + + RpcResult 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 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 = 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 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 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 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 getInterfaceIdentifier(InterfaceKey interfaceKey) { + InstanceIdentifier.InstanceIdentifierBuilder interfaceInstanceIdentifierBuilder = + InstanceIdentifier.builder(Interfaces.class).child(Interface.class, interfaceKey); + return interfaceInstanceIdentifierBuilder.build(); + } + + public static Interface getInterfaceFromConfigDS(InterfaceKey interfaceKey, DataBroker dataBroker) { + InstanceIdentifier interfaceId = getInterfaceIdentifier(interfaceKey); + Optional interfaceOptional = IfmUtil.read(LogicalDatastoreType.CONFIGURATION, interfaceId, dataBroker); + if (!interfaceOptional.isPresent()) { + return null; + } + + return interfaceOptional.get(); + } + + /** + * Delete l2 gateway device ucast local macs from elan.
+ * 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> deleteL2GatewayDeviceUcastLocalMacsFromElan( + L2GatewayDevice l2GatewayDevice, String elanName) { + List> futures = new ArrayList<>(); + + ElanInstance elan = ElanUtils.getElanInstanceByName(elanName); + if (elan == null) { + LOG.error("Could not find Elan by name: {}", elanName); + return futures; + } + + List lstLocalUcastMacs = l2GatewayDevice.getUcastLocalMacs(); + if (lstLocalUcastMacs != null) { + for (LocalUcastMacs localUcastMac : lstLocalUcastMacs) { + List 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 lstMac = Lists.transform(lstLocalUcastMacs, new Function() { + @Override + public MacAddress apply(LocalUcastMacs mac) { + return (mac != null) ? mac.getMacEntryKey() : null; + } + }); + + ConcurrentMap 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> result = itmRpcService.addL2GwDevice(builder.build()); + RpcResult 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 index 00000000..be0012f4 --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/l2gw/utils/L2GatewayConnectionUtils.java @@ -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 inst = InstanceIdentifier.create(Neutron.class).child(L2gateways.class) + .child(L2gateway.class, new L2gatewayKey(l2GatewayId)); + Optional l2Gateway = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst); + if (l2Gateway.isPresent()) { + return l2Gateway.get(); + } + return null; + } + + public static List getL2gatewayList(DataBroker broker) { + InstanceIdentifier inst = InstanceIdentifier.create(Neutron.class).child(L2gateways.class); + Optional l2gateways = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst); + + if (l2gateways.isPresent()) { + return l2gateways.get().getL2gateway(); + } + return null; + } + + public static List getAllL2gatewayConnections(DataBroker broker) { + InstanceIdentifier inst = InstanceIdentifier.create(Neutron.class) + .child(L2gatewayConnections.class); + Optional 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 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 iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId)); + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE, + bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid)); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @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 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 iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId)); + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE, + bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid)); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @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>> { + 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> call() throws Exception { + List> 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 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>>() { + + @Override + public List> call() { + List> futures = new ArrayList<>(); + futures.add(ElanL2GatewayUtils.installElanMacsInL2GatewayDevice( + logicalSwitchName, l2GatewayDevice)); + return futures; + }} + ); + } + + return futures; + } + + private ListenableFuture 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 path = HwvtepSouthboundUtils + .createLogicalSwitchesInstanceIdentifier(hwvtepNodeId, new HwvtepNodeName(logicalSwitchName)); + LogicalSwitches logicalSwitch = HwvtepSouthboundUtils.createLogicalSwitch(logicalSwitchName, + elanInstance.getDescription(), segmentationId); + + ListenableFuture lsCreateFuture = HwvtepUtils.addLogicalSwitch(broker, hwvtepNodeId, logicalSwitch); + Futures.addCallback(lsCreateFuture, new FutureCallback() { + @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>> { + 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> call() throws Exception { + List> 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 index 00000000..8f96d642 --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitor.java @@ -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 index 00000000..83a00f39 --- /dev/null +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/statusanddiag/ElanStatusMonitorMBean.java @@ -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(); + +} diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanConstants.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanConstants.java index fb3634eb..51aff882 100755 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanConstants.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanConstants.java @@ -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"; } diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanUtils.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanUtils.java index 201eef26..e81a2759 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanUtils.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/vpnservice/elan/utils/ElanUtils.java @@ -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 DEFAULT_CALLBACK = - new FutureCallback() { - 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 DEFAULT_CALLBACK = new FutureCallback() { + @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> result = idManager.allocateId(getIdInput); RpcResult 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> result = idManager.releaseId(releaseIdInput); @@ -153,12 +201,11 @@ public class ElanUtils { public static Optional read(DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier path) { - - ReadOnlyTransaction tx = broker.newReadOnlyTransaction(); - + ReadOnlyTransaction tx = (broker != null ) ? broker.newReadOnlyTransaction() : dataBroker.newReadOnlyTransaction(); Optional result = Optional.absent(); try { - result = tx.read(datastoreType, path).get(); + CheckedFuture, 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 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 elanIdentifierId = getElanInstanceConfigurationDataPath(elanInstanceName); - Optional elanInstance = read(broker, LogicalDatastoreType.CONFIGURATION, elanIdentifierId); + Optional 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 elanInterfaceId = getElanInterfaceConfigurationDataPathId(elanInterfaceName); - Optional existingElanInterface = read(broker, LogicalDatastoreType.CONFIGURATION, elanInterfaceId); + Optional 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 elanIdentifier = getElanInstanceOperationalDataPath(elanInstanceName); - Optional elanInstance = read(broker, LogicalDatastoreType.OPERATIONAL, elanIdentifier); + Optional 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 existingMacEntryId = getInterfaceMacEntriesIdentifierOperationalDataPath(interfaceName, physAddress); - Optional existingInterfaceMacEntry = read(broker, LogicalDatastoreType.OPERATIONAL, existingMacEntryId); + Optional 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 existingInterfaceMacEntry = read(broker, LogicalDatastoreType.OPERATIONAL, identifier); + Optional 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 macId = getMacEntryOperationalDataPath(elanName, physAddress); - Optional existingElanMacEntry = read(broker, LogicalDatastoreType.OPERATIONAL, macId); + Optional 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 existingInterfaceMacEntry = read(broker, LogicalDatastoreType.OPERATIONAL, identifier); + Optional 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 elanInterfaceId = getElanInterfaceMacEntriesOperationalDataPath(interfaceName); - Optional existingElanInterface = read(broker, LogicalDatastoreType.OPERATIONAL, elanInterfaceId); + Optional 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 getElanInterfaceMacAddresses(String interfaceName) { + List macAddresses = new ArrayList(); + ElanInterfaceMac elanInterfaceMac = ElanUtils.getElanInterfaceMacByInterfaceName(interfaceName); + if (elanInterfaceMac != null && elanInterfaceMac.getMacEntry() != null) { + List macEntries = elanInterfaceMac.getMacEntry(); + for (MacEntry macEntry : macEntries) { + macAddresses.add(macEntry.getMacAddress()); + } + } + return macAddresses; + } + public static InstanceIdentifier 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 elanDpnInterfacesId = getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId); - Optional elanDpnInterfaces = read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfacesId); - if(elanDpnInterfaces.isPresent()) { + InstanceIdentifier elanDpnInterfacesId = + getElanDpnInterfaceOperationalDataPath(elanInstanceName, dpId); + Optional elanDpnInterfaces = read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfacesId); + if ( elanDpnInterfaces.isPresent() ) { return elanDpnInterfaces.get(); } return null; } - public static InstanceIdentifier 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 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 elanId = getElanInfoEntriesOperationalDataPath(elanTag); - Optional existingElanInfo = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanId); + Optional 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 getInterfaceInfoByInterfaceTag(long interfaceTag) { InstanceIdentifier interfaceId = getInterfaceInfoEntriesOperationalDataPath(interfaceTag); - Optional 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 getInterfaceInfoEntriesOperationalDataPath(long interfaceTag) { @@ -347,23 +422,78 @@ public class ElanUtils { public static InstanceIdentifier 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 elanDpnInterfaceId = getElanDpnOperationDataPath(elanName); - Optional existingElanDpnInterfaces = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId); + Optional existingElanDpnInterfaces = + ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId); if(existingElanDpnInterfaces.isPresent()) { return existingElanDpnInterfaces.get(); } return null; } - public static ElanDpnInterfaces getElanDpnInterfacesList() { - DataBroker broker = elanServiceProvider.getBroker(); - InstanceIdentifier elanDpnInterfaceId = InstanceIdentifier.builder(ElanDpnInterfaces.class).build(); - Optional 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 getParticipatingDPNsInElanInstance(String elanInstanceName) { + List dpIds = new ArrayList(); + InstanceIdentifier elanDpnInterfaceId = getElanDpnOperationDataPath(elanInstanceName); + Optional existingElanDpnInterfaces = + ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId); + if (!existingElanDpnInterfaces.isPresent()) { + return dpIds; + } + List 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 elanDpnInterfaceId = getElanDpnOperationDataPath(elanInstanceName); + Optional existingElanDpnInterfaces = + ElanUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, elanDpnInterfaceId); + if (!existingElanDpnInterfaces.isPresent()) { + return isDpIdPresent; + } + List dpnInterfaces = existingElanDpnInterfaces.get().getDpnInterfaces(); + for (DpnInterfaces dpnInterface: dpnInterfaces) { + if (dpnInterface.getDpId().equals(dpId)) { + isDpIdPresent = true; + break; + } + } + return isDpIdPresent; + } + + public static ElanDpnInterfaces getElanDpnInterfacesList() { + InstanceIdentifier elanDpnInterfaceId = + InstanceIdentifier.builder(ElanDpnInterfaces.class).build(); + Optional 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 elanForwardingTableId = InstanceIdentifier.builder(ElanForwardingTables.class).build(); - Optional existingElanForwardingList = ElanUtils.read(broker, LogicalDatastoreType.OPERATIONAL, elanForwardingTableId); + InstanceIdentifier elanForwardingTableId = + InstanceIdentifier.builder(ElanForwardingTables.class).build(); + Optional 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 elanMacTableId = getElanMacTableOperationalDataPath(elanName); + Optional 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 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 getTunnelIdMatchForFilterEqualsLPortTag(int lportTag) { List mkMatches = new ArrayList(); // Matching metadata mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] { - BigInteger.valueOf(LportTag)})); + BigInteger.valueOf(lportTag)})); return mkMatches; - - } - public static List 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 getInstructionsInPortForOutGroup(String ifName) { List mkInstructions = new ArrayList(); - List actionsInfos = new ArrayList (); - 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 actions) { - return new InstructionBuilder().setInstruction(new WriteActionsCaseBuilder().setWriteActions(new WriteActionsBuilder().setAction(actions).build()).build()).setKey(new InstructionKey(0)).build(); - } - - public static Instruction getApplyActionInstruction(List actions) { - return new InstructionBuilder().setInstruction(new ApplyActionsCaseBuilder().setApplyActions(new ApplyActionsBuilder().setAction(actions).build()).build()).setKey(new InstructionKey(0)).build(); - } - - - public static List getEgressActionsForInterface(String ifName) { + List 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 getEgressActionsForInterface(String ifName, Long tunnelKey) { List listAction = new ArrayList(); try { + GetEgressActionsForInterfaceInput getEgressActionInput = + new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(tunnelKey).build(); Future> result = - elanServiceProvider.getInterfaceManagerRpcService().getEgressActionsForInterface( - new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).build()); + interfaceMgrRpcService.getEgressActionsForInterface(getEgressActionInput); RpcResult 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 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 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 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 getInvolvedDpnsInElan(String elanName) { List 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 mkMatches = new ArrayList(); - 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 mkInstructions = new ArrayList(); - List actionsInfos = new ArrayList (); - 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 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 mkMatches = new ArrayList(); - 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 mkInstructions = new ArrayList(); - //List of ActionInfo for the provided Source and Destination DPIDs + //List of Action for the provided Source and Destination DPIDs try { - List 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 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 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 = InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(nodeId)).build(); - Optional nodePresent = read(broker, LogicalDatastoreType.OPERATIONAL, node); - if(nodePresent.isPresent()) { - isPresent = true; - } - return isPresent; + + InstanceIdentifier node = InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(nodeId)) + .build(); + Optional 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 void delete(DataBroker broker, LogicalDatastoreType datastoreType, - InstanceIdentifier path, FutureCallback callback) { + InstanceIdentifier path, FutureCallback callback) { WriteTransaction tx = broker.newWriteOnlyTransaction(); tx.delete(datastoreType, path); Futures.addCallback(tx.submit(), callback); } public static void syncWrite(DataBroker broker, LogicalDatastoreType datastoreType, - InstanceIdentifier path, T data) { + InstanceIdentifier path, T data) { WriteTransaction tx = broker.newWriteOnlyTransaction(); tx.put(datastoreType, path, data, true); CheckedFuture futures = tx.submit(); @@ -750,7 +1034,7 @@ public class ElanUtils { public static BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority, - BigInteger cookie, List instructions) { + BigInteger cookie, List 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 buildItmEgressActions(String tunnelIfaceName, Long tunnelKey) { + List result = EMPTY_LIST; + if (tunnelIfaceName != null && !tunnelIfaceName.isEmpty()) { + GetEgressActionsForInterfaceInput getEgressActInput = new GetEgressActionsForInterfaceInputBuilder() + .setIntfName(tunnelIfaceName).setTunnelKey(tunnelKey).build(); + + Future> 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 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 actions = new ArrayList<>(); - String tunnelInterfaceName; - GetTunnelInterfaceNameInput input = new GetTunnelInterfaceNameInputBuilder().setDestinationDpid(destinationDpnId).setSourceDpid(sourceDpnId).build(); - Future> 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 getExternalItmEgressAction(BigInteger srcDpnId, NodeId torNode, long vni ) { + List result = EMPTY_LIST; + + GetExternalTunnelInterfaceNameInput input = new GetExternalTunnelInterfaceNameInputBuilder() + .setDestinationNode(torNode.getValue()).setSourceNode(srcDpnId.toString()).build(); + Future> 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> result = - interfaceManagerRpcService.getEgressActionsForInterface(getEgressActionInput); - RpcResult 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 getInternalItmEgressAction(BigInteger sourceDpnId, BigInteger destinationDpnId, + long serviceTag) { + List result = EMPTY_LIST; + + logger.debug("In getInternalItmEgressAction Action source {}, destination {}, elanTag {}", + sourceDpnId, destinationDpnId, serviceTag); + + GetTunnelInterfaceNameInput input = new GetTunnelInterfaceNameInputBuilder() + .setDestinationDpid(destinationDpnId).setSourceDpid(sourceDpnId).build(); + Future> 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 getTunnelMatchesForServiceId(int elanTag) { List mkMatches = new ArrayList(); // 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> futureObject = itmRpcService.removeTerminatingServiceActions(input); try { @@ -827,20 +1183,286 @@ public class ElanUtils { } public static void createTerminatingServiceActions(BigInteger destDpId, int serviceId, List actions) { - ItmRpcService itmRpcService = elanServiceProvider.getItmRpcService(); List mkInstructions = new ArrayList(); - 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 tunnelListInstanceIdentifier = InstanceIdentifier.builder(TunnelList.class).build(); - Optional tunnelList = read(dataBroker, LogicalDatastoreType.CONFIGURATION, tunnelListInstanceIdentifier); - if(tunnelList.isPresent()) { + Optional 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> installDmacFlowsToExternalRemoteMac(BigInteger dpnId, + String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName) { + List> 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 buildMatchesForElanTagShFlagAndDstMac(long elanTag, boolean shFlag, String macAddr) { + List mkMatches = new ArrayList(); + 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 mkMatches = buildMatchesForElanTagShFlagAndDstMac(elanTag, /*shFlag*/ false, dstMacAddress); + List mkInstructions = new ArrayList(); + try { + List 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 mkMatches = buildMatchesForElanTagShFlagAndDstMac(elanTag, /*shFlag*/ true, dstMacAddress); + List 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 mkMatches = buildMatchesForElanTagShFlagAndDstMac(elanTag, /*shFlag*/ false, macAddress); + + List mkInstructions = new ArrayList(); + + try { + //List of Action for the provided Source and Destination DPIDs + List 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> deleteDmacFlowsToExternalMac(long elanTag, BigInteger dpId, + String extDeviceNodeId, String macToRemove ) { + List> 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> output = interfaceMgrRpcService + .getDpidFromInterface(new GetDpidFromInterfaceInputBuilder().setIntfName(interfaceName).build()); + try { + RpcResult 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; + } + } + diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModule.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModule.java index 4df938c9..82b13ea4 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModule.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModule.java @@ -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; } diff --git a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModuleFactory.java b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModuleFactory.java index 618f8c00..3baee245 100644 --- a/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModuleFactory.java +++ b/elanmanager/elanmanager-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/elanservice/impl/rev150216/ElanServiceImplModuleFactory.java @@ -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 { diff --git a/elanmanager/elanmanager-impl/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/elanmanager/elanmanager-impl/src/main/resources/OSGI-INF/blueprint/blueprint.xml index 20a3ed25..a2da3ff7 100644 --- a/elanmanager/elanmanager-impl/src/main/resources/OSGI-INF/blueprint/blueprint.xml +++ b/elanmanager/elanmanager-impl/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -63,5 +63,9 @@ + + + + \ No newline at end of file diff --git a/elanmanager/elanmanager-impl/src/main/yang/elanservice-impl.yang b/elanmanager/elanmanager-impl/src/main/yang/elanservice-impl.yang index 6af446e7..90ff1150 100644 --- a/elanmanager/elanmanager-impl/src/main/yang/elanservice-impl.yang +++ b/elanmanager/elanmanager-impl/src/main/yang/elanservice-impl.yang @@ -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 diff --git a/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepSouthboundUtils.java b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepSouthboundUtils.java index 72bb09d9..c1dae6c4 100644 --- a/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepSouthboundUtils.java +++ b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepSouthboundUtils.java @@ -109,12 +109,12 @@ public class HwvtepSouthboundUtils { public static InstanceIdentifier createRemoteUcastMacsInstanceIdentifier(NodeId nodeId, String logicalSwitchName, MacAddress mac) { - InstanceIdentifier logicalSwitch = createLogicalSwitchesInstanceIdentifier(nodeId, + InstanceIdentifier 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 createLocalUcastMacsInstanceIdentifier(NodeId nodeId, String logicalSwitchName, MacAddress mac) { - InstanceIdentifier logicalSwitch = createLogicalSwitchesInstanceIdentifier(nodeId, + InstanceIdentifier 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 createRemoteMcastMacsInstanceIdentifier(NodeId nodeId, - String logicalSwitchName, - MacAddress mac) { - InstanceIdentifier logicalSwitch = createLogicalSwitchesInstanceIdentifier(nodeId, + String logicalSwitchName, MacAddress mac) { + InstanceIdentifier 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)); } /** diff --git a/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepUtils.java b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepUtils.java index f913f62c..64756366 100644 --- a/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepUtils.java +++ b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/hwvtep/HwvtepUtils.java @@ -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 iid = HwvtepSouthboundUtils + .createPhysicalLocatorInstanceIdentifier(nodeId, phyLocatorAug) + .augmentation(HwvtepPhysicalLocatorAugmentation.class); + Optional 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 iid = HwvtepSouthboundUtils.createInstanceIdentifier(nodeId).augmentation(HwvtepGlobalAugmentation.class) - .child(RemoteUcastMacs.class, new RemoteUcastMacsKey(remoteUcastMac.getLogicalSwitchRef(), remoteUcastMac.getMacEntryKey())); + InstanceIdentifier 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 iid = HwvtepSouthboundUtils + .createRemoteMcastMacsInstanceIdentifier(nodeId, remoteMcastMacsKey); + Optional optRemoteMcastMac = MDSALUtil.read(broker, datastoreType, iid); + if (optRemoteMcastMac.isPresent()) { + return optRemoteMcastMac.get(); + } + return null; + } + /** * Delete remote mcast mac from config DS. * diff --git a/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/L2GatewayDevice.java b/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/L2GatewayDevice.java index 6e2c4da5..9b218278 100644 --- a/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/L2GatewayDevice.java +++ b/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/L2GatewayDevice.java @@ -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 ucastLocalMacs = Collections.synchronizedList(new ArrayList()); + /** 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) * diff --git a/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronNetworkChangeListener.java b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronNetworkChangeListener.java index fd3111ae..d432f45c 100644 --- a/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronNetworkChangeListener.java +++ b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronNetworkChangeListener.java @@ -101,8 +101,7 @@ public class NeutronNetworkChangeListener extends AbstractDataChangeListener } 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); diff --git a/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronvpnUtils.java b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronvpnUtils.java index f9e7e3d3..3acc5ce4 100644 --- a/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronvpnUtils.java +++ b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/NeutronvpnUtils.java @@ -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 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 networkType = providerSegment.getNetworkType(); + return (networkType != null && networkType.isAssignableFrom(NetworkTypeVxlan.class)); + } } -- 2.36.6