From 2ef3711b19264a9edfa95713d7bb33b79b699712 Mon Sep 17 00:00:00 2001 From: Shashidhar R Date: Fri, 1 Apr 2016 12:27:04 +0530 Subject: [PATCH] L2 Gw create changes related to ITM Tunnels creation in neutronvpn module Change-Id: Ib5508bab11a8528030d0752c9b8b189a6471da5a Signed-off-by: Shashidhar R --- .../utils/SystemPropertyReader.java | 30 ++ .../utils/clustering/ClusteringUtils.java | 86 +++++- .../EntityOwnerNotPresentException.java | 20 ++ .../neutronvpn/api/l2gw/L2GatewayDevice.java | 270 ++++++++++++++++++ .../api/l2gw/utils/L2GatewayCacheUtils.java | 41 +++ neutronvpn/neutronvpn-impl/pom.xml | 10 + .../src/main/config/default-config.xml | 9 + .../NeutronNetworkChangeListener.java | 16 +- .../neutronvpn/NeutronvpnProvider.java | 17 ++ .../neutronvpn/NeutronvpnUtils.java | 10 + .../neutronvpn/l2gw/L2GatewayListener.java | 236 +++++++++++++++ .../neutronvpn/l2gw/L2GatewayProvider.java | 37 +++ .../neutronvpn/l2gw/L2GatewayUtils.java | 82 ++++++ .../impl/rev150325/NeutronvpnImplModule.java | 2 + .../src/main/yang/neutronvpn-impl.yang | 19 ++ 15 files changed, 865 insertions(+), 20 deletions(-) create mode 100644 mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/SystemPropertyReader.java create mode 100644 mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/clustering/EntityOwnerNotPresentException.java create mode 100644 neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/L2GatewayDevice.java create mode 100644 neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/utils/L2GatewayCacheUtils.java create mode 100644 neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayListener.java create mode 100644 neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayProvider.java create mode 100644 neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayUtils.java diff --git a/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/SystemPropertyReader.java b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/SystemPropertyReader.java new file mode 100644 index 00000000..b5fb187f --- /dev/null +++ b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/SystemPropertyReader.java @@ -0,0 +1,30 @@ +/* + * 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.utils; + +import org.opendaylight.yangtools.util.PropertyUtils; + +public class SystemPropertyReader { + public static class Cluster { + // Sleep time to be used between successive EntityOwnershipState calls + // Returned time is in milliseconds + public static long getSleepTimeBetweenRetries() { + return Long.getLong("cluster.entity_owner.sleep_time_between_retries", 1000); + } + + // Returns max. retries to be tried with EntityOwnershipState calls + public static int getMaxRetries() { + return Integer.getInteger("cluster.entity_owner.max_retries", 5); + } + } + + // Returns max retries to be tried with DataStoreJobCoordinator calls + public static int getDataStoreJobCoordinatorMaxRetries() { + return Integer.getInteger("datastore.job_coordinator.max_retries", 5); + } +} diff --git a/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/clustering/ClusteringUtils.java b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/clustering/ClusteringUtils.java index ec90e2c5..311efac7 100644 --- a/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/clustering/ClusteringUtils.java +++ b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/clustering/ClusteringUtils.java @@ -8,30 +8,86 @@ package org.opendaylight.vpnservice.utils.clustering; import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; + import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + import org.opendaylight.controller.md.sal.common.api.clustering.Entity; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipState; +import org.opendaylight.vpnservice.datastoreutils.DataStoreJobCoordinator; +import org.opendaylight.vpnservice.utils.SystemPropertyReader; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; public class ClusteringUtils { - public static boolean isNodeEntityOwner(EntityOwnershipService entityOwnershipService, String entityType, - String nodeId) { - Entity entity = new Entity(entityType, nodeId); - Optional entityState = entityOwnershipService.getOwnershipState(entity); - if (entityState.isPresent()) { - return entityState.get().isOwner(); - } - return false; + public static ListenableFuture checkNodeEntityOwner(EntityOwnershipService entityOwnershipService, + String entityType, String nodeId) { + return checkNodeEntityOwner(entityOwnershipService, new Entity(entityType, nodeId), + SystemPropertyReader.Cluster.getSleepTimeBetweenRetries(), SystemPropertyReader.Cluster.getMaxRetries()); } - public static boolean isNodeEntityOwner(EntityOwnershipService entityOwnershipService, String entityType, - YangInstanceIdentifier nodeId) { - Entity entity = new Entity(entityType, nodeId); - Optional entityState = entityOwnershipService.getOwnershipState(entity); - if (entityState.isPresent()) { - return entityState.get().isOwner(); + public static ListenableFuture checkNodeEntityOwner(EntityOwnershipService entityOwnershipService, + String entityType, YangInstanceIdentifier nodeId) { + return checkNodeEntityOwner(entityOwnershipService, new Entity(entityType, nodeId), + SystemPropertyReader.Cluster.getSleepTimeBetweenRetries(), SystemPropertyReader.Cluster.getMaxRetries()); + } + + public static ListenableFuture checkNodeEntityOwner(EntityOwnershipService entityOwnershipService, + Entity entity, long sleepBetweenRetries, int maxRetries) { + SettableFuture checkNodeEntityfuture = SettableFuture.create(); + DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance(); + CheckEntityOwnerTask checkEntityOwnerTask = new CheckEntityOwnerTask(entityOwnershipService, entity, + checkNodeEntityfuture, sleepBetweenRetries, maxRetries); + dataStoreCoordinator.enqueueJob(entityOwnershipService.toString(), checkEntityOwnerTask); + return checkNodeEntityfuture; + } + + private static class CheckEntityOwnerTask implements Callable>> { + EntityOwnershipService entityOwnershipService; + Entity entity; + SettableFuture checkNodeEntityfuture; + long sleepBetweenRetries; + int retries; + + public CheckEntityOwnerTask(EntityOwnershipService entityOwnershipService, Entity entity, + SettableFuture checkNodeEntityfuture, long sleepBetweenRetries, int retries) { + this.entityOwnershipService = entityOwnershipService; + this.entity = entity; + this.checkNodeEntityfuture = checkNodeEntityfuture; + this.sleepBetweenRetries = sleepBetweenRetries; + this.retries = retries; + } + + @Override + public List> call() throws Exception { + while (retries > 0) { + retries = retries - 1; + Optional entityState = entityOwnershipService.getOwnershipState(entity); + if (entityState.isPresent()) { + EntityOwnershipState entityOwnershipState = entityState.get(); + if (entityOwnershipState.hasOwner()) { + checkNodeEntityfuture.set(entityOwnershipState.isOwner()); + return getResultFuture(); + } + } + Thread.sleep(sleepBetweenRetries); + } + checkNodeEntityfuture.setException(new EntityOwnerNotPresentException("Entity Owner Not Present")); + return getResultFuture(); + } + + private List> getResultFuture() { + ListenableFuture future = Futures.immediateFuture(null); + ArrayList> futureList = Lists.newArrayList(); + futureList.add(future); + return futureList; } - return false; } } diff --git a/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/clustering/EntityOwnerNotPresentException.java b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/clustering/EntityOwnerNotPresentException.java new file mode 100644 index 00000000..afaea70c --- /dev/null +++ b/mdsalutil/mdsalutil-api/src/main/java/org/opendaylight/vpnservice/utils/clustering/EntityOwnerNotPresentException.java @@ -0,0 +1,20 @@ +/* + * 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.utils.clustering; + +public class EntityOwnerNotPresentException extends Exception { + + private static final long serialVersionUID = 1L; + + public EntityOwnerNotPresentException() { + } + + public EntityOwnerNotPresentException(String message) { + super(message); + } +} 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 new file mode 100644 index 00000000..6e2c4da5 --- /dev/null +++ b/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/L2GatewayDevice.java @@ -0,0 +1,270 @@ +/* + * 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.neutronvpn.api.l2gw; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +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.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LocalUcastMacs; + +/** + * The Class L2GatewayDevice. + */ +public class L2GatewayDevice { + + /** The device name. */ + String deviceName; + + /** The hwvtep node id. */ + String hwvtepNodeId; + + /** The tunnel ips. */ + List tunnelIps = new ArrayList(); + + /** The l2 gateway ids. */ + List l2GatewayIds = new ArrayList(); + + /** The ucast local macs. */ + List ucastLocalMacs = Collections.synchronizedList(new ArrayList()); + + /** + * VTEP device name mentioned with L2 Gateway. + * + * @return the device name + */ + public String getDeviceName() { + return deviceName; + } + + /** + * Sets the device name. + * + * @param deviceName + * the new device name + */ + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + /** + * VTEP Node id for the device mentioned with L2 Gateway. + * + * @return the hwvtep node id + */ + public String getHwvtepNodeId() { + return hwvtepNodeId; + } + + /** + * Sets the hwvtep node id. + * + * @param nodeId + * the new hwvtep node id + */ + public void setHwvtepNodeId(String nodeId) { + this.hwvtepNodeId = nodeId; + } + + /** + * Tunnel IP created with in the device mentioned with L2 Gateway. + * + * @return the tunnel ips + */ + public List getTunnelIps() { + return tunnelIps; + } + + /** + * Gets the tunnel ip. + * + * @return the tunnel ip + */ + public IpAddress getTunnelIp() { + if (tunnelIps.size() > 0) { + return tunnelIps.get(0); + } + return null; + } + + /** + * Adds the tunnel ip. + * + * @param tunnelIp + * the tunnel ip + */ + public void addTunnelIp(IpAddress tunnelIp) { + tunnelIps.add(tunnelIp); + } + + /** + * UUID representing L2Gateway. + * + * @return the l2 gateway ids + */ + public List getL2GatewayIds() { + return l2GatewayIds; + } + + /** + * Adds the l2 gateway id. + * + * @param l2GatewayId + * the l2 gateway id + */ + public void addL2GatewayId(Uuid l2GatewayId) { + l2GatewayIds.add(l2GatewayId); + } + + /** + * Removes the l2 gateway id. + * + * @param l2GatewayId + * the l2 gateway id + */ + public void removeL2GatewayId(Uuid l2GatewayId) { + l2GatewayIds.remove(l2GatewayId); + } + + /** + * Clear hwvtep node data. + */ + public void clearHwvtepNodeData() { + tunnelIps.clear(); + hwvtepNodeId = null; + } + + /** + * Sets the tunnel ips. + * + * @param tunnelIps + * the new tunnel ips + */ + public void setTunnelIps(List tunnelIps) { + this.tunnelIps = tunnelIps; + } + + /** + * Gets the ucast local macs. + * + * @return the ucast local macs + */ + public List getUcastLocalMacs() { + return new ArrayList<>(ucastLocalMacs); + } + + /** + * Adds the ucast local mac. + * + * @param localUcastMacs + * the local ucast macs + */ + public void addUcastLocalMac(LocalUcastMacs localUcastMacs) { + ucastLocalMacs.add(localUcastMacs); + } + + /** + * Removes the ucast local mac. + * + * @param localUcastMacs + * the local ucast macs + */ + public void removeUcastLocalMac(LocalUcastMacs localUcastMacs) { + ucastLocalMacs.remove(localUcastMacs); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((deviceName == null) ? 0 : deviceName.hashCode()); + result = prime * result + ((hwvtepNodeId == null) ? 0 : hwvtepNodeId.hashCode()); + result = prime * result + ((l2GatewayIds == null) ? 0 : l2GatewayIds.hashCode()); + result = prime * result + ((tunnelIps == null) ? 0 : tunnelIps.hashCode()); + result = prime * result + ((ucastLocalMacs == null) ? 0 : ucastLocalMacs.hashCode()); + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + L2GatewayDevice other = (L2GatewayDevice) obj; + if (deviceName == null) { + if (other.deviceName != null) { + return false; + } + } else if (!deviceName.equals(other.deviceName)) { + return false; + } + if (hwvtepNodeId == null) { + if (other.hwvtepNodeId != null) { + return false; + } + } else if (!hwvtepNodeId.equals(other.hwvtepNodeId)) { + return false; + } + if (l2GatewayIds == null) { + if (other.l2GatewayIds != null) { + return false; + } + } else if (!l2GatewayIds.equals(other.l2GatewayIds)) { + return false; + } + if (tunnelIps == null) { + if (other.tunnelIps != null) { + return false; + } + } else if (!tunnelIps.equals(other.tunnelIps)) { + return false; + } + if (ucastLocalMacs == null) { + if (other.ucastLocalMacs != null) { + return false; + } + } else if (!ucastLocalMacs.equals(other.ucastLocalMacs)) { + return false; + } + return true; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("L2GatewayDevice [deviceName=").append(deviceName).append(", hwvtepNodeId=").append(hwvtepNodeId) + .append(", tunnelIps=").append(tunnelIps).append(", l2GatewayIds=").append(l2GatewayIds) + .append(", ucastLocalMacs=").append(ucastLocalMacs).append("]"); + return builder.toString(); + } + +} diff --git a/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/utils/L2GatewayCacheUtils.java b/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/utils/L2GatewayCacheUtils.java new file mode 100644 index 00000000..d941dec9 --- /dev/null +++ b/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/l2gw/utils/L2GatewayCacheUtils.java @@ -0,0 +1,41 @@ +/* + * 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.neutronvpn.api.l2gw.utils; + +import java.util.concurrent.ConcurrentMap; + +import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice; +import org.opendaylight.vpnservice.utils.cache.CacheUtil; + +public class L2GatewayCacheUtils { + public static final String L2GATEWAY_CACHE_NAME = "L2GW"; + + public static void createL2DeviceCache() { + if (CacheUtil.getCache(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME) == null) { + CacheUtil.createCache(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME); + } + } + + public static void addL2DeviceToCache(String devicename, L2GatewayDevice l2GwDevice) { + ConcurrentMap cachedMap = (ConcurrentMap) CacheUtil + .getCache(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME); + cachedMap.put(devicename, l2GwDevice); + } + + public static L2GatewayDevice removeL2DeviceFromCache(String devicename) { + ConcurrentMap cachedMap = (ConcurrentMap) CacheUtil + .getCache(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME); + return cachedMap.remove(devicename); + } + + public static L2GatewayDevice getL2DeviceFromCache(String devicename) { + ConcurrentMap cachedMap = (ConcurrentMap) CacheUtil + .getCache(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME); + return cachedMap.get(devicename); + } +} diff --git a/neutronvpn/neutronvpn-impl/pom.xml b/neutronvpn/neutronvpn-impl/pom.xml index 922a727b..934c6b04 100644 --- a/neutronvpn/neutronvpn-impl/pom.xml +++ b/neutronvpn/neutronvpn-impl/pom.xml @@ -52,6 +52,16 @@ and is available at http://www.eclipse.org/legal/epl-v10.html lockmanager-api ${vpnservices.version} + + org.opendaylight.vpnservice + itm-api + ${vpnservices.version} + + + org.opendaylight.ovsdb + hwvtepsouthbound-api + ${vpns.ovsdb.version} + diff --git a/neutronvpn/neutronvpn-impl/src/main/config/default-config.xml b/neutronvpn/neutronvpn-impl/src/main/config/default-config.xml index 13b91f09..86f904e5 100644 --- a/neutronvpn/neutronvpn-impl/src/main/config/default-config.xml +++ b/neutronvpn/neutronvpn-impl/src/main/config/default-config.xml @@ -11,6 +11,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html urn:opendaylight:params:xml:ns:yang:neutronvpn:impl?module=neutronvpn-impl&revision=2015-03-25 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: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 @@ -38,6 +39,14 @@ and is available at http://www.eclipse.org/legal/epl-v10.html mdsalutil-service + + entity-ownership:entity-ownership-service + entity-ownership-service + + + binding:binding-normalized-node-serializer + runtime-mapping-singleton + 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 4a474f35..fd3111ae 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 @@ -76,7 +76,7 @@ public class NeutronNetworkChangeListener extends AbstractDataChangeListener getNeutronRouterSubnetIds(DataBroker broker, Uuid routerId) { logger.info("getNeutronRouterSubnetIds for {}", routerId.getValue()); diff --git a/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayListener.java b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayListener.java new file mode 100644 index 00000000..f62d3f43 --- /dev/null +++ b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayListener.java @@ -0,0 +1,236 @@ +/* + * 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.neutronvpn.l2gw; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +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.yangtools.yang.common.RpcResult; +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.controller.md.sal.binding.api.ClusteredDataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService; +import org.opendaylight.vpnservice.utils.clustering.ClusteringUtils; +import org.opendaylight.vpnservice.datastoreutils.AsyncClusteredDataChangeListenerBase; +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.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.opendaylight.neutron.rev150712.Neutron; +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.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.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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; + +public class L2GatewayListener extends AsyncClusteredDataChangeListenerBase + implements AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(L2GatewayListener.class); + + private ListenerRegistration listenerRegistration; + private final DataBroker broker; + private ItmRpcService itmRpcService; + private EntityOwnershipService entityOwnershipService; + private BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer; + + public L2GatewayListener(final DataBroker db, RpcProviderRegistry rpcRegistry, + EntityOwnershipService entityOwnershipService, + BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer) { + super(L2gateway.class, L2GatewayListener.class); + broker = db; + this.entityOwnershipService = entityOwnershipService; + this.bindingNormalizedNodeSerializer = bindingNormalizedNodeSerializer; + itmRpcService = rpcRegistry.getRpcService(ItmRpcService.class); + 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 listener Closed"); + } + + private void registerListener(final DataBroker db) { + try { + listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, + InstanceIdentifier.create(Neutron.class).child(L2gateways.class).child(L2gateway.class), + L2GatewayListener.this, DataChangeScope.SUBTREE); + } catch (final Exception e) { + LOG.error("Neutron Manager L2 Gateway DataChange listener registration fail!", e); + throw new IllegalStateException("Neutron Manager L2 Gateway DataChange listener registration failed.", e); + } + } + + @Override + protected void add(final InstanceIdentifier identifier, final L2gateway input) { + LOG.info("Adding L2gateway with ID: {}", input.getUuid()); + + List l2Devices = input.getDevices(); + for (Devices l2Device : l2Devices) { + if (LOG.isTraceEnabled()) { + LOG.trace("Adding L2gateway device: {}", l2Device); + } + addL2Device(l2Device, input); + } + } + + @Override + protected void remove(final InstanceIdentifier identifier, final L2gateway input) { + LOG.info("Removing L2gateway with ID: {}", input.getUuid()); + + List l2Devices = input.getDevices(); + for (Devices l2Device : l2Devices) { + if (LOG.isTraceEnabled()) { + LOG.trace("Removing L2gateway device: {}", l2Device); + } + removeL2Device(l2Device, input); + } + } + + @Override + protected void update(InstanceIdentifier identifier, L2gateway original, L2gateway update) { + if (LOG.isTraceEnabled()) { + LOG.trace("Updating L2gateway : key: " + identifier + ", original value=" + original + ", update value=" + + update); + } + } + + private void addL2Device(Devices l2Device, L2gateway input) { + final String l2DeviceName = l2Device.getDeviceName(); + L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(l2DeviceName); + if (l2GwDevice != null) { + if (!L2GatewayUtils.isGatewayAssociatedToL2Device(l2GwDevice) + && L2GatewayUtils.isL2GwDeviceConnected(l2GwDevice)) { + // VTEP already discovered; create ITM tunnels + final String hwvtepId = l2GwDevice.getHwvtepNodeId(); + InstanceIdentifier iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId)); + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE, + bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid)); + final List tunnelIps = l2GwDevice.getTunnelIps(); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @Override + public void onSuccess(Boolean isOwner) { + if (isOwner) { + LOG.info("Creating ITM Tunnels for {} connected to cluster node owner", l2DeviceName); + for (IpAddress tunnelIp : tunnelIps) { + L2GatewayUtils.createItmTunnels(itmRpcService, hwvtepId, l2DeviceName, tunnelIp); + } + } else { + LOG.info("ITM Tunnels are not created on the cluster node as this is not owner for {}", + l2DeviceName); + } + } + + @Override + public void onFailure(Throwable error) { + LOG.error("Failed to create ITM tunnels", error); + } + }); + } else { + LOG.trace("ITM tunnels are already created for device {}", l2DeviceName); + } + } else { + LOG.trace("{} is not connected; ITM tunnels will be created when device comes up", l2DeviceName); + // Pre-provision scenario. Create L2GatewayDevice without VTEP + // details for pushing configurations as soon as device discovered + l2GwDevice = new L2GatewayDevice(); + l2GwDevice.setDeviceName(l2DeviceName); + L2GatewayCacheUtils.addL2DeviceToCache(l2DeviceName, l2GwDevice); + } + l2GwDevice.addL2GatewayId(input.getUuid()); + } + + private void removeL2Device(Devices l2Device, L2gateway input) { + final String l2DeviceName = l2Device.getDeviceName(); + L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(l2DeviceName); + if (l2GwDevice != null) { + // Delete ITM tunnels if it's last Gateway deleted and device is connected + // Also, do not delete device from cache if it's connected + if (L2GatewayUtils.isLastL2GatewayBeingDeleted(l2GwDevice)) { + if (L2GatewayUtils.isL2GwDeviceConnected(l2GwDevice)) { + l2GwDevice.removeL2GatewayId(input.getUuid()); + // Delete ITM tunnels + final String hwvtepId = l2GwDevice.getHwvtepNodeId(); + InstanceIdentifier iid = HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(hwvtepId)); + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + entityOwnershipService, HwvtepSouthboundConstants.HWVTEP_ENTITY_TYPE, + bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid)); + final List tunnelIps = l2GwDevice.getTunnelIps(); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @Override + public void onSuccess(Boolean isOwner) { + if (isOwner) { + LOG.info("Deleting ITM Tunnels for {} connected to cluster node owner", l2DeviceName); + for (IpAddress tunnelIp : tunnelIps) { + L2GatewayUtils.deleteItmTunnels(itmRpcService, hwvtepId, l2DeviceName, tunnelIp); + } + } else { + LOG.info("ITM Tunnels are not deleted on the cluster node as this is not owner for {}", + l2DeviceName); + } + } + + @Override + public void onFailure(Throwable error) { + LOG.error("Failed to delete ITM tunnels", error); + } + }); + } else { + L2GatewayCacheUtils.removeL2DeviceFromCache(l2DeviceName); + } + } else { + l2GwDevice.removeL2GatewayId(input.getUuid()); + LOG.trace("ITM tunnels are not deleted for {} as this device has other L2gateway associations", + l2DeviceName); + } + } else { + LOG.error("Unable to find L2 Gateway details for {}", l2DeviceName); + } + } + + @Override + protected InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(L2gateway.class); + } + + @Override + protected ClusteredDataChangeListener getDataChangeListener() { + return L2GatewayListener.this; + } + + @Override + protected DataChangeScope getDataChangeScope() { + return AsyncDataBroker.DataChangeScope.BASE; + } +} diff --git a/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayProvider.java b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayProvider.java new file mode 100644 index 00000000..2da2a459 --- /dev/null +++ b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayProvider.java @@ -0,0 +1,37 @@ +/* + * 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.neutronvpn.l2gw; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.vpnservice.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService; +import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class L2GatewayProvider { + private static final Logger LOG = LoggerFactory.getLogger(L2GatewayProvider.class); + + private L2GatewayListener l2GatewayListener; + + public L2GatewayProvider(DataBroker broker, RpcProviderRegistry rpcRegistry, + EntityOwnershipService entityOwnershipService, + BindingNormalizedNodeSerializer bindingNormalizedNodeSerializer) { + L2GatewayCacheUtils.createL2DeviceCache(); + l2GatewayListener = new L2GatewayListener(broker, rpcRegistry, entityOwnershipService, + bindingNormalizedNodeSerializer); + LOG.info("L2GatewayProvider Initialized"); + } + + public void close() throws Exception { + l2GatewayListener.close(); + LOG.info("L2GatewayProvider Closed"); + } +} diff --git a/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayUtils.java b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayUtils.java new file mode 100644 index 00000000..43de51ca --- /dev/null +++ b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/vpnservice/neutronvpn/l2gw/L2GatewayUtils.java @@ -0,0 +1,82 @@ +/* + * 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.neutronvpn.l2gw; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice; +import org.opendaylight.vpnservice.utils.hwvtep.HwvtepSouthboundConstants; +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.opendaylight.vpnservice.itm.rpcs.rev151217.AddL2GwDeviceInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.DeleteL2GwDeviceInputBuilder; +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.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class L2GatewayUtils { + private static final Logger LOG = LoggerFactory.getLogger(L2GatewayUtils.class); + + protected static boolean isGatewayAssociatedToL2Device(L2GatewayDevice l2GwDevice) { + return (l2GwDevice.getL2GatewayIds().size() > 0); + } + + protected static boolean isLastL2GatewayBeingDeleted(L2GatewayDevice l2GwDevice) { + return (l2GwDevice.getL2GatewayIds().size() == 1); + } + + protected static boolean isL2GwDeviceConnected(L2GatewayDevice l2GwDevice) { + return (l2GwDevice.getHwvtepNodeId() != null); + } + + protected static boolean isItmTunnelsCreatedForL2Device(L2GatewayDevice l2GwDevice) { + return (l2GwDevice.getHwvtepNodeId() != null && l2GwDevice.getL2GatewayIds().size() > 0); + } + + protected 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); + } + } + + protected static void deleteItmTunnels(ItmRpcService itmRpcService, String hwvtepId, String psName, + IpAddress tunnelIp) { + DeleteL2GwDeviceInputBuilder builder = new DeleteL2GwDeviceInputBuilder(); + builder.setTopologyId(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID.getValue()); + builder.setNodeId(HwvtepSouthboundUtils.createManagedNodeId(new NodeId(hwvtepId), psName).getValue()); + builder.setIpAddress(tunnelIp); + try { + Future> result = itmRpcService.deleteL2GwDevice(builder.build()); + RpcResult rpcResult = result.get(); + if (rpcResult.isSuccessful()) { + LOG.info("Deleted ITM tunnels for {}", hwvtepId); + } else { + LOG.error("Failed to delete ITM Tunnels: ", rpcResult.getErrors()); + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("RPC to delete ITM tunnels failed", e); + } + } +} diff --git a/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/neutronvpn/impl/rev150325/NeutronvpnImplModule.java b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/neutronvpn/impl/rev150325/NeutronvpnImplModule.java index 9752c8b9..1510b39a 100644 --- a/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/neutronvpn/impl/rev150325/NeutronvpnImplModule.java +++ b/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/neutronvpn/impl/rev150325/NeutronvpnImplModule.java @@ -37,6 +37,8 @@ public class NeutronvpnImplModule extends org.opendaylight.yang.gen.v1.urn.opend NeutronvpnProvider provider = new NeutronvpnProvider(getRpcRegistryDependency()); provider.setMdsalManager(getMdsalutilDependency()); provider.setLockManager(lockManagerService); + provider.setEntityOwnershipService(getEntityOwnershipServiceDependency()); + provider.setBindingNormalizedNodeSerializer(getBindingNormalizedNodeSerializerDependency()); getBrokerDependency().registerProvider(provider); return provider; } diff --git a/neutronvpn/neutronvpn-impl/src/main/yang/neutronvpn-impl.yang b/neutronvpn/neutronvpn-impl/src/main/yang/neutronvpn-impl.yang index dafd23ad..bbf1201c 100644 --- a/neutronvpn/neutronvpn-impl/src/main/yang/neutronvpn-impl.yang +++ b/neutronvpn/neutronvpn-impl/src/main/yang/neutronvpn-impl.yang @@ -6,7 +6,9 @@ module neutronvpn-impl { import config { prefix config; revision-date 2013-04-05; } import neutronvpn-api { prefix neutronvpn-api; revision-date 2015-08-12;} 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 odl-mdsalutil { prefix odl-mdsal; revision-date 2015-04-10;} + import opendaylight-entity-ownership-service { prefix eos; revision-date 2015-08-10;} description "Service definition for neutronvpn project"; @@ -49,6 +51,23 @@ module neutronvpn-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; + } + } + } } } } -- 2.36.6