From: Kency Kurian Date: Fri, 22 Apr 2016 10:04:52 +0000 (+0530) Subject: DHCP Handling for TOR VM X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=vpnservice.git;a=commitdiff_plain;h=9909ff254706d57ff52ced55f83e2859257b7d6d DHCP Handling for TOR VM - As the TOR floods the packets to the CSS mesh, some logic should be added such that only one CSS punts the DHCP packet to the controller and the other CSS drops the DHCP packet. - The designated CSS is chosen for each TOR-ELAN pair and it is stored in the datastore. Change-Id: I695f1a7600938c1e4f6411f9752de20bf44349cd Signed-off-by: Kency Kurian --- diff --git a/dhcpservice/dhcpservice-api/src/main/java/org/opendaylight/vpnservice/dhcpservice/api/DHCPUtils.java b/dhcpservice/dhcpservice-api/src/main/java/org/opendaylight/vpnservice/dhcpservice/api/DHCPUtils.java index 167e8077..99f7a7f6 100644 --- a/dhcpservice/dhcpservice-api/src/main/java/org/opendaylight/vpnservice/dhcpservice/api/DHCPUtils.java +++ b/dhcpservice/dhcpservice-api/src/main/java/org/opendaylight/vpnservice/dhcpservice/api/DHCPUtils.java @@ -86,4 +86,14 @@ public abstract class DHCPUtils { return result; } + public static String byteArrayToString(byte[] bytes) { + StringBuilder str = new StringBuilder(); + for (byte b : bytes) { + str.append(Integer.toHexString((b >>> 4) & 0x0F)); + str.append(Integer.toHexString(b & 0x0F)); + str.append(":"); + } + str.deleteCharAt(str.lastIndexOf(":")); + return str.toString(); + } } diff --git a/dhcpservice/dhcpservice-api/src/main/yang/dhcp.yang b/dhcpservice/dhcpservice-api/src/main/yang/dhcp.yang new file mode 100644 index 00000000..14b721c6 --- /dev/null +++ b/dhcpservice/dhcpservice-api/src/main/yang/dhcp.yang @@ -0,0 +1,34 @@ +module vpnservice-dhcp { + namespace "urn:opendaylight:vpnservice:dhcp"; + prefix "dhcp"; + + import ietf-inet-types { + prefix inet; + revision-date "2010-09-24"; + } + + revision "2016-04-28" { + description "It provides required datastore containers to handle DHCP requests + coming from access or external tunnel ports"; + } + + container designated-switches-for-external-tunnels { + config true; + description "contains designated dataplane-node-identifier which handles DHCP requests for each external tunnel"; + list designated-switch-for-tunnel { + key "tunnel-remote-ip-address elan-instance-name"; + leaf tunnel-remote-ip-address { + description "remote end point ip address of external tunnel"; + type inet:ip-address; + } + leaf elan-instance-name { + description "elan name indicates l2 network domain"; + type string; + } + leaf dpId { + description "contains dataplane-node-identifier"; + type int64; + } + } + } +} \ No newline at end of file diff --git a/dhcpservice/dhcpservice-impl/pom.xml b/dhcpservice/dhcpservice-impl/pom.xml index cff44a5f..a5a5ce67 100644 --- a/dhcpservice/dhcpservice-impl/pom.xml +++ b/dhcpservice/dhcpservice-impl/pom.xml @@ -64,6 +64,21 @@ and is available at http://www.eclipse.org/legal/epl-v10.html interfacemgr-api ${vpnservices.version} + + ${project.groupId} + elanmanager-api + ${vpnservices.version} + + + org.opendaylight.vpnservice + itm-api + ${vpnservices.version} + + + org.opendaylight.ovsdb + hwvtepsouthbound-api + ${vpns.ovsdb.version} + diff --git a/dhcpservice/dhcpservice-impl/src/main/config/default-config.xml b/dhcpservice/dhcpservice-impl/src/main/config/default-config.xml index fe6008a4..988288d6 100644 --- a/dhcpservice/dhcpservice-impl/src/main/config/default-config.xml +++ b/dhcpservice/dhcpservice-impl/src/main/config/default-config.xml @@ -12,6 +12,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html urn:opendaylight:params:xml:ns:yang:dhcpservice:impl?module=dhcpservice-impl&revision=2015-07-10 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 urn:opendaylight:params:xml:ns:yang:neutronvpn:api?module=neutronvpn-api&revision=2015-08-12 @@ -41,6 +42,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html neutronvpn:neutronvpn-api neutronvpn + + entity-ownership:entity-ownership-service + entity-ownership-service + diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpDesignatedDpnListener.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpDesignatedDpnListener.java new file mode 100644 index 00000000..838cc468 --- /dev/null +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpDesignatedDpnListener.java @@ -0,0 +1,99 @@ +/* + * 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.dhcpservice; + +import java.math.BigInteger; +import java.util.List; + +import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener; +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.AsyncDataBroker.DataChangeScope; +import org.opendaylight.vpnservice.datastoreutils.AsyncClusteredDataChangeListenerBase; +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.dhcp.rev160428.DesignatedSwitchesForExternalTunnels; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DhcpDesignatedDpnListener extends AsyncClusteredDataChangeListenerBase implements AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(DhcpDesignatedDpnListener.class); + private ListenerRegistration listenerRegistration; + private DhcpExternalTunnelManager dhcpExternalTunnelManager; + private DataBroker broker; + + public DhcpDesignatedDpnListener(final DhcpExternalTunnelManager dhcpExternalTunnelManager, final DataBroker broker) { + super(DesignatedSwitchForTunnel.class, DhcpDesignatedDpnListener.class); + this.dhcpExternalTunnelManager = dhcpExternalTunnelManager; + this.broker = broker; + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + LOG.error("Error when cleaning up DhcpDesignatedDpnListener.", e); + } + listenerRegistration = null; + } + LOG.debug("DhcpDesignatedDpnListener Listener Closed"); + } + + @Override + protected void remove(InstanceIdentifier identifier, DesignatedSwitchForTunnel del) { + dhcpExternalTunnelManager.removeFromLocalCache(BigInteger.valueOf(del.getDpId()), del.getTunnelRemoteIpAddress(), del.getElanInstanceName()); + dhcpExternalTunnelManager.unInstallDhcpFlowsForVms(del.getElanInstanceName(), del.getTunnelRemoteIpAddress(), DhcpServiceUtils.getListOfDpns(broker)); + } + + @Override + protected void update(InstanceIdentifier identifier, DesignatedSwitchForTunnel original, + DesignatedSwitchForTunnel update) { + BigInteger designatedDpnId = BigInteger.valueOf(update.getDpId()); + IpAddress tunnelRemoteIpAddress = update.getTunnelRemoteIpAddress(); + String elanInstanceName = update.getElanInstanceName(); + dhcpExternalTunnelManager.removeFromLocalCache(BigInteger.valueOf(original.getDpId()), original.getTunnelRemoteIpAddress(), original.getElanInstanceName()); + dhcpExternalTunnelManager.updateLocalCache(designatedDpnId, tunnelRemoteIpAddress, elanInstanceName); +/* List elanDpns = DhcpServiceUtils.getDpnsForElan(elanInstanceName, broker); + if (elanDpns == null || elanDpns.isEmpty()) { + dhcpExternalTunnelManager.installRemoteMcastMac(designatedDpnId, tunnelRemoteIpAddress, elanInstanceName); + }*/ + } + + @Override + protected void add(InstanceIdentifier identifier, DesignatedSwitchForTunnel add) { + BigInteger designatedDpnId = BigInteger.valueOf(add.getDpId()); + IpAddress tunnelRemoteIpAddress = add.getTunnelRemoteIpAddress(); + String elanInstanceName = add.getElanInstanceName(); + dhcpExternalTunnelManager.updateLocalCache(designatedDpnId, tunnelRemoteIpAddress, elanInstanceName); +/* List elanDpns = DhcpServiceUtils.getDpnsForElan(elanInstanceName, broker); + if (elanDpns == null || elanDpns.isEmpty()) { + dhcpExternalTunnelManager.installRemoteMcastMac(designatedDpnId, tunnelRemoteIpAddress, elanInstanceName); + }*/ + } + + @Override + protected InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(DesignatedSwitchesForExternalTunnels.class).child(DesignatedSwitchForTunnel.class); + } + + @Override + protected ClusteredDataChangeListener getDataChangeListener() { + return DhcpDesignatedDpnListener.this; + } + + @Override + protected DataChangeScope getDataChangeScope() { + return AsyncDataBroker.DataChangeScope.SUBTREE; + } +} diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpExternalTunnelManager.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpExternalTunnelManager.java new file mode 100644 index 00000000..b51c8342 --- /dev/null +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpExternalTunnelManager.java @@ -0,0 +1,662 @@ +/* + * 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.dhcpservice; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +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.vpnservice.dhcpservice.api.DHCPMConstants; +import org.opendaylight.vpnservice.mdsalutil.MDSALDataStoreUtils; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.mdsalutil.NwConstants; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice; +import org.opendaylight.vpnservice.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils; +import org.opendaylight.vpnservice.neutronvpn.api.utils.NeutronUtils; +import org.opendaylight.vpnservice.utils.clustering.ClusteringUtils; +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.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus; +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.Uuid; +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.rev150712.Neutron; +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.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.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.rev160428.DesignatedSwitchesForExternalTunnels; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.dhcp.rev160428.designated.switches._for.external.tunnels.DesignatedSwitchForTunnelKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.IfTunnel; +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.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint; +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.Optional; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +public class DhcpExternalTunnelManager { + + private static final Logger logger = LoggerFactory.getLogger(DhcpExternalTunnelManager.class); + public static final String UNKNOWN_DMAC = "00:00:00:00:00:00"; + private static final FutureCallback DEFAULT_CALLBACK = + new FutureCallback() { + @Override + public void onSuccess(Void result) { + logger.debug("Success in Datastore write operation"); + } + @Override + public void onFailure(Throwable error) { + logger.error("Error in Datastore write operation", error); + }; + }; + private final DataBroker broker; + private IMdsalApiManager mdsalUtil; + + private ConcurrentMap>> designatedDpnsToTunnelIpElanNameCache = new ConcurrentHashMap>>(); + private ConcurrentMap, Set> tunnelIpElanNameToVmMacCache = new ConcurrentHashMap, Set>(); + private ConcurrentMap, Port> vniMacAddressToPortCache = new ConcurrentHashMap, Port>(); + private ItmRpcService itmRpcService; + private EntityOwnershipService entityOwnershipService; + + public DhcpExternalTunnelManager(DataBroker broker, IMdsalApiManager mdsalUtil, ItmRpcService itmRpcService, EntityOwnershipService entityOwnershipService) { + this.broker = broker; + this.mdsalUtil = mdsalUtil; + this.itmRpcService = itmRpcService; + this.entityOwnershipService = entityOwnershipService; + initilizeCaches(); + } + + private void initilizeCaches() { + logger.trace("Loading designatedDpnsToTunnelIpElanNameCache"); + InstanceIdentifier instanceIdentifier = InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).build(); + Optional designatedSwitchForTunnelOptional = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier); + if (designatedSwitchForTunnelOptional.isPresent()) { + List list = designatedSwitchForTunnelOptional.get().getDesignatedSwitchForTunnel(); + for (DesignatedSwitchForTunnel designatedSwitchForTunnel : list) { + List> listOfTunnelIpElanNamePair = designatedDpnsToTunnelIpElanNameCache.get(designatedSwitchForTunnel.getDpId()); + if (listOfTunnelIpElanNamePair == null) { + listOfTunnelIpElanNamePair = new LinkedList>(); + } + Pair tunnelIpElanNamePair = new ImmutablePair(designatedSwitchForTunnel.getTunnelRemoteIpAddress(), designatedSwitchForTunnel.getElanInstanceName()); + listOfTunnelIpElanNamePair.add(tunnelIpElanNamePair); + designatedDpnsToTunnelIpElanNameCache.put(BigInteger.valueOf(designatedSwitchForTunnel.getDpId()), listOfTunnelIpElanNamePair); + } + } + logger.trace("Loading vniMacAddressToPortCache"); + InstanceIdentifier inst = InstanceIdentifier.builder(Neutron.class).child(Ports.class).build(); + Optional optionalPorts = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst); + if (optionalPorts.isPresent()) { + List list = optionalPorts.get().getPort(); + for (Port port : list) { + if(NeutronUtils.isPortVnicTypeNormal(port)) { + continue; + } + String macAddress = port.getMacAddress(); + Uuid networkId = port.getNetworkId(); + String segmentationId = DhcpServiceUtils.getSegmentationId(networkId, broker); + if (segmentationId == null) { + return; + } + updateVniMacToPortCache(new BigInteger(segmentationId), macAddress, port); + } + } + + } + + + public BigInteger designateDpnId(IpAddress tunnelIp, + String elanInstanceName, List dpns) { + BigInteger designatedDpnId = readDesignatedSwitchesForExternalTunnel(tunnelIp, elanInstanceName); + if (designatedDpnId != null && !designatedDpnId.equals(DHCPMConstants.INVALID_DPID)) { + logger.trace("Dpn {} already designated for tunnelIp - elan : {} - {}", designatedDpnId, tunnelIp, elanInstanceName); + return designatedDpnId; + } + return chooseDpn(tunnelIp, elanInstanceName, dpns); + } + + public void installDhcpFlowsForVms(IpAddress tunnelIp, String elanInstanceName, List dpns, + BigInteger designatedDpnId, String vmMacAddress) { + installDhcpEntries(designatedDpnId, vmMacAddress, entityOwnershipService); + dpns.remove(designatedDpnId); + for (BigInteger dpn : dpns) { + installDhcpDropAction(dpn, vmMacAddress, entityOwnershipService); + } + updateLocalCache(tunnelIp, elanInstanceName, vmMacAddress); + } + + public void installDhcpFlowsForVms(IpAddress tunnelIp, + String elanInstanceName, BigInteger designatedDpnId, Set listVmMacAddress) { + for (String vmMacAddress : listVmMacAddress) { + installDhcpEntries(designatedDpnId, vmMacAddress); + } + } + + public void unInstallDhcpFlowsForVms(String elanInstanceName, List dpns, String vmMacAddress) { + for (BigInteger dpn : dpns) { + unInstallDhcpEntries(dpn, vmMacAddress, entityOwnershipService); + } + removeFromLocalCache(elanInstanceName, vmMacAddress); + } + + public BigInteger readDesignatedSwitchesForExternalTunnel(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 BigInteger.valueOf(designatedSwitchForTunnelOptional.get().getDpId()); + } + return null; + } + + public void writeDesignatedSwitchForExternalTunnel(BigInteger dpnId, IpAddress tunnelIp, String elanInstanceName) { + DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey = new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp); + InstanceIdentifier instanceIdentifier = InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build(); + DesignatedSwitchForTunnel designatedSwitchForTunnel = new DesignatedSwitchForTunnelBuilder().setDpId(dpnId.longValue()).setElanInstanceName(elanInstanceName).setTunnelRemoteIpAddress(tunnelIp).setKey(designatedSwitchForTunnelKey).build(); + logger.trace("Writing into CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp, elanInstanceName, dpnId); + MDSALDataStoreUtils.asyncUpdate(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier, designatedSwitchForTunnel, DEFAULT_CALLBACK); + } + + public void removeDesignatedSwitchForExternalTunnel(BigInteger dpnId, IpAddress tunnelIp, String elanInstanceName) { + DesignatedSwitchForTunnelKey designatedSwitchForTunnelKey = new DesignatedSwitchForTunnelKey(elanInstanceName, tunnelIp); + InstanceIdentifier instanceIdentifier = InstanceIdentifier.builder(DesignatedSwitchesForExternalTunnels.class).child(DesignatedSwitchForTunnel.class, designatedSwitchForTunnelKey).build(); + logger.trace("Writing into CONFIG DS tunnelIp {}, elanInstanceName {}, dpnId {}", tunnelIp, elanInstanceName, dpnId); + MDSALDataStoreUtils.asyncRemove(broker, LogicalDatastoreType.CONFIGURATION, instanceIdentifier, DEFAULT_CALLBACK); + } + + public void installDhcpDropActionOnDpn(BigInteger dpId) { + List vmMacs = getAllVmMacs(); + logger.trace("Installing drop actions to this new DPN {} VMs {}", dpId, vmMacs); + for (String vmMacAddress : vmMacs) { + installDhcpDropAction(dpId, vmMacAddress); + } + } + + private List getAllVmMacs() { + List vmMacs = new LinkedList(); + Collection> listOfVmMacs = tunnelIpElanNameToVmMacCache.values(); + for (Set list : listOfVmMacs) { + vmMacs.addAll(list); + } + return vmMacs; + } + + public void updateLocalCache(BigInteger designatedDpnId, IpAddress tunnelIp, String elanInstanceName) { + Pair tunnelIpElanName = new ImmutablePair(tunnelIp, elanInstanceName); + List> tunnelIpElanNameList; + tunnelIpElanNameList = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId); + if (tunnelIpElanNameList == null) { + tunnelIpElanNameList = new LinkedList>(); + } + tunnelIpElanNameList.add(tunnelIpElanName); + designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameList); + } + + private void updateLocalCache(IpAddress tunnelIp, String elanInstanceName, String vmMacAddress) { + Pair tunnelIpElanName = new ImmutablePair(tunnelIp, elanInstanceName); + Set listExistingVmMacAddress; + listExistingVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanName); + if (listExistingVmMacAddress == null) { + listExistingVmMacAddress = new HashSet(); + } + listExistingVmMacAddress.add(vmMacAddress); + tunnelIpElanNameToVmMacCache.put(tunnelIpElanName, listExistingVmMacAddress); + } + + public void handleDesignatedDpnDown(BigInteger dpnId, List listOfDpns) { + logger.trace("In handleDesignatedDpnDown dpnId {}, listOfDpns {}", dpnId, listOfDpns); + try { + List> listOfTunnelIpElanNamePairs = designatedDpnsToTunnelIpElanNameCache.get(dpnId); + if (!dpnId.equals(DHCPMConstants.INVALID_DPID)) { + List listOfVms = getAllVmMacs(); + for (String vmMacAddress : listOfVms) { + unInstallDhcpEntries(dpnId, vmMacAddress); + } + } + if (listOfTunnelIpElanNamePairs == null || listOfTunnelIpElanNamePairs.isEmpty()) { + logger.trace("No tunnelIpElanName to handle for dpn {}. Returning", dpnId); + return; + } + for (Pair pair : listOfTunnelIpElanNamePairs) { + updateCacheAndInstallNewFlows(dpnId, listOfDpns, pair); + } + } catch (Exception e) { + logger.error("Error in handleDesignatedDpnDown {}", e); + } + } + + public void updateCacheAndInstallNewFlows(BigInteger dpnId, + List listOfDpns, Pair pair) + throws ExecutionException { + BigInteger newDesignatedDpn = chooseDpn(pair.getLeft(), pair.getRight(), listOfDpns); + if (newDesignatedDpn.equals(DHCPMConstants.INVALID_DPID)) { + return; + } + Set listVmMacs = tunnelIpElanNameToVmMacCache.get(pair); + if (listVmMacs != null && !listVmMacs.isEmpty()) { + installDhcpFlowsForVms(pair.getLeft(), pair.getRight(), newDesignatedDpn, listVmMacs); + } + } + + private void changeExistingFlowToDrop(Pair tunnelIpElanNamePair, BigInteger dpnId) { + try { + Set listVmMacAddress = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair); + if (listVmMacAddress == null || listVmMacAddress.isEmpty()) { + return; + } + for (String vmMacAddress : listVmMacAddress) { + installDhcpDropAction(dpnId, vmMacAddress); + } + } catch (Exception e) { + logger.error("Error in uninstallExistingFlows {}", e); + } + } + + /** + * Choose a dpn among the list of elanDpns such that it has lowest count of being the designated dpn. + * @param tunnelIp + * @param elanInstanceName + * @param dpns + * @return + */ + private BigInteger chooseDpn(IpAddress tunnelIp, String elanInstanceName, + List dpns) { + BigInteger designatedDpnId = DHCPMConstants.INVALID_DPID; + if (dpns != null && dpns.size() != 0) { + List candidateDpns = DhcpServiceUtils.getDpnsForElan(elanInstanceName, broker); + candidateDpns.retainAll(dpns); + logger.trace("Choosing new dpn for tunnelIp {}, elanInstanceName {}, among elanDpns {}", tunnelIp, elanInstanceName, candidateDpns); + boolean elanDpnAvailableFlag = true; + if (candidateDpns == null || candidateDpns.isEmpty()) { + candidateDpns = dpns; + elanDpnAvailableFlag = false; + } + int size = 0; + L2GatewayDevice device = getDeviceFromTunnelIp(elanInstanceName, tunnelIp); + if (device == null) { + logger.trace("Could not find any device for elanInstanceName {} and tunnelIp {}", elanInstanceName, tunnelIp); + handleUnableToDesignateDpn(tunnelIp, elanInstanceName); + return designatedDpnId; + } + for (BigInteger dpn : candidateDpns) { + String hwvtepNodeId = device.getHwvtepNodeId(); + if (!elanDpnAvailableFlag) { + if (!isTunnelConfigured(dpn, hwvtepNodeId)) { + logger.trace("Tunnel is not configured on dpn {} to TOR {}", dpn, hwvtepNodeId); + continue; + } + } else if (!isTunnelUp(hwvtepNodeId, dpn)) { + logger.trace("Tunnel is not up between dpn {} and TOR {}", dpn, hwvtepNodeId); + continue; + } + List> tunnelIpElanNameList = designatedDpnsToTunnelIpElanNameCache.get(dpn); + if (tunnelIpElanNameList == null) { + designatedDpnId = dpn; + break; + } + if (size == 0 || tunnelIpElanNameList.size() < size) { + size = tunnelIpElanNameList.size(); + designatedDpnId = dpn; + } + } + if (!elanDpnAvailableFlag) { + installRemoteMcastMac(designatedDpnId, device, elanInstanceName); + } + writeDesignatedSwitchForExternalTunnel(designatedDpnId, tunnelIp, elanInstanceName); + return designatedDpnId; + } + handleUnableToDesignateDpn(tunnelIp, elanInstanceName); + return designatedDpnId; + } + + private void handleUnableToDesignateDpn(IpAddress tunnelIp, String elanInstanceName) { + writeDesignatedSwitchForExternalTunnel(DHCPMConstants.INVALID_DPID, tunnelIp, elanInstanceName); + } + + public void installDhcpEntries(BigInteger dpnId, String vmMacAddress) { + DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL, vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil); + } + + public void installDhcpEntries(final BigInteger dpnId, final String vmMacAddress, EntityOwnershipService eos) { + final String nodeId = DhcpServiceUtils.getNodeIdFromDpnId(dpnId); + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + eos, MDSALUtil.NODE_PREFIX, nodeId); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @Override + public void onSuccess(Boolean isOwner) { + if (isOwner) { + installDhcpEntries(dpnId, vmMacAddress); + } else { + logger.trace("Exiting installDhcpEntries since this cluster node is not the owner for dpn {}", nodeId); + } + } + + @Override + public void onFailure(Throwable error) { + logger.error("Error while fetching checkNodeEntityOwner", error); + } + }); + } + + public void unInstallDhcpEntries(BigInteger dpnId, String vmMacAddress) { + DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL, vmMacAddress, NwConstants.DEL_FLOW, mdsalUtil); + } + + public void unInstallDhcpEntries(final BigInteger dpnId, final String vmMacAddress, EntityOwnershipService eos) { + final String nodeId = DhcpServiceUtils.getNodeIdFromDpnId(dpnId); + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + eos, MDSALUtil.NODE_PREFIX, nodeId); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @Override + public void onSuccess(Boolean isOwner) { + if (isOwner) { + unInstallDhcpEntries(dpnId, vmMacAddress); + } else { + logger.trace("Exiting unInstallDhcpEntries since this cluster node is not the owner for dpn {}", nodeId); + } + } + + @Override + public void onFailure(Throwable error) { + logger.error("Error while fetching checkNodeEntityOwner", error); + } + }); + } + + public void installDhcpDropAction(BigInteger dpn, String vmMacAddress) { + DhcpServiceUtils.setupDhcpDropAction(dpn, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL, vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil); + } + + public void installDhcpDropAction(final BigInteger dpnId, final String vmMacAddress, EntityOwnershipService eos) { + final String nodeId = DhcpServiceUtils.getNodeIdFromDpnId(dpnId); + ListenableFuture checkEntityOwnerFuture = ClusteringUtils.checkNodeEntityOwner( + eos, MDSALUtil.NODE_PREFIX, nodeId); + Futures.addCallback(checkEntityOwnerFuture, new FutureCallback() { + @Override + public void onSuccess(Boolean isOwner) { + if (isOwner) { + installDhcpDropAction(dpnId, vmMacAddress); + } else { + logger.trace("Exiting installDhcpDropAction since this cluster node is not the owner for dpn {}", nodeId); + } + } + + @Override + public void onFailure(Throwable error) { + logger.error("Error while fetching checkNodeEntityOwner", error); + } + }); + } + + public void handleTunnelStateDown(IpAddress tunnelIp, BigInteger interfaceDpn) { + logger.trace("In handleTunnelStateDown tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn); + if (interfaceDpn == null) { + return; + } + try { + List> tunnelElanPairList = designatedDpnsToTunnelIpElanNameCache.get(interfaceDpn); + if (tunnelElanPairList == null || tunnelElanPairList.isEmpty()) { + return; + } + for (Pair tunnelElanPair : tunnelElanPairList) { + IpAddress tunnelIpInDpn = tunnelElanPair.getLeft(); + if (tunnelIpInDpn.equals(tunnelIp)) { + List dpns = DhcpServiceUtils.getListOfDpns(broker); + dpns.remove(interfaceDpn); + changeExistingFlowToDrop(tunnelElanPair, interfaceDpn); + updateCacheAndInstallNewFlows(interfaceDpn, dpns, tunnelElanPair); + } + } + } catch (Exception e) { + logger.error("Error in handleTunnelStateDown {}", e.getMessage()); + logger.trace("Exception details {}", e); + } + } + + private void removeFromLocalCache(String elanInstanceName, String vmMacAddress) { + Set> tunnelIpElanNameKeySet = tunnelIpElanNameToVmMacCache.keySet(); + for (Pair pair : tunnelIpElanNameKeySet) { + if (pair.getRight().trim().equalsIgnoreCase(elanInstanceName.trim())) { + Set listExistingVmMacAddress; + listExistingVmMacAddress = tunnelIpElanNameToVmMacCache.get(pair); + if (listExistingVmMacAddress == null || listExistingVmMacAddress.isEmpty()) { + continue; + } + listExistingVmMacAddress.remove(vmMacAddress); + if (listExistingVmMacAddress.size() > 0) { + tunnelIpElanNameToVmMacCache.put(pair, listExistingVmMacAddress); + return; + } + tunnelIpElanNameToVmMacCache.remove(pair); + } + } + } + + public void removeFromLocalCache(BigInteger designatedDpnId, IpAddress tunnelIp, String elanInstanceName) { + Pair tunnelIpElanName = new ImmutablePair(tunnelIp, elanInstanceName); + List> tunnelIpElanNameList; + tunnelIpElanNameList = designatedDpnsToTunnelIpElanNameCache.get(designatedDpnId); + if (tunnelIpElanNameList != null) { + tunnelIpElanNameList.remove(tunnelIpElanName); + if (tunnelIpElanNameList.size() != 0) { + designatedDpnsToTunnelIpElanNameCache.put(designatedDpnId, tunnelIpElanNameList); + } else { + designatedDpnsToTunnelIpElanNameCache.remove(designatedDpnId); + } + } + } + + public void updateVniMacToPortCache(BigInteger vni, String macAddress, Port port) { + if (macAddress == null) { + return; + } + Pair vniMacAddressPair = new ImmutablePair(vni, macAddress.toUpperCase()); + logger.trace("Updating vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}", vni, macAddress.toUpperCase(), vniMacAddressPair, port); + vniMacAddressToPortCache.put(vniMacAddressPair, port); + } + + public void removeVniMacToPortCache(BigInteger vni, String macAddress) { + if (macAddress == null) { + return; + } + Pair vniMacAddressPair = new ImmutablePair(vni, macAddress.toUpperCase()); + vniMacAddressToPortCache.remove(vniMacAddressPair); + } + + public Port readVniMacToPortCache(BigInteger vni, String macAddress) { + if (macAddress == null) { + return null; + } + Pair vniMacAddressPair = new ImmutablePair(vni, macAddress.toUpperCase()); + logger.trace("Reading vniMacAddressToPortCache with vni {} , mac {} , pair {} and port {}", vni, macAddress.toUpperCase(), vniMacAddressPair, vniMacAddressToPortCache.get(vniMacAddressPair)); + return vniMacAddressToPortCache.get(vniMacAddressPair); + } + + public 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(); + logger.debug("Tunnel interface name: {}", tunnelInterfaceName); + } else { + logger.warn("RPC call to ITM.GetExternalTunnelInterfaceName failed with error: {}", + rpcResult.getErrors()); + } + } catch (NullPointerException | InterruptedException | ExecutionException e) { + logger.error("Failed to get external tunnel interface name for sourceNode: {} and dstNode: {}: {} ", + sourceNode, dstNode, e); + } + return tunnelInterfaceName; + } + + public static Optional getNode(DataBroker dataBroker, String physicalSwitchNodeId) { + InstanceIdentifier psNodeId = HwvtepSouthboundUtils + .createInstanceIdentifier(new NodeId(physicalSwitchNodeId)); + Optional physicalSwitchOptional = MDSALUtil.read(LogicalDatastoreType.CONFIGURATION, psNodeId, dataBroker); + return physicalSwitchOptional; + } + + public RemoteMcastMacs createRemoteMcastMac(Node dstDevice, String logicalSwitchName, IpAddress internalTunnelIp) { + List locators = new ArrayList<>(); + for (TerminationPoint tp : dstDevice.getTerminationPoint()) { + HwvtepPhysicalLocatorAugmentation aug = tp.getAugmentation(HwvtepPhysicalLocatorAugmentation.class); + if (internalTunnelIp.getIpv4Address().equals(aug.getDstIp().getIpv4Address())) { + HwvtepPhysicalLocatorRef phyLocRef = new HwvtepPhysicalLocatorRef( + HwvtepSouthboundUtils.createPhysicalLocatorInstanceIdentifier(dstDevice.getNodeId(), aug)); + locators.add(new LocatorSetBuilder().setLocatorRef(phyLocRef).build()); + } + } + HwvtepLogicalSwitchRef lsRef = new HwvtepLogicalSwitchRef(HwvtepSouthboundUtils + .createLogicalSwitchesInstanceIdentifier(dstDevice.getNodeId(), new HwvtepNodeName(logicalSwitchName))); + + RemoteMcastMacs remoteUcastMacs = new RemoteMcastMacsBuilder() + .setMacEntryKey(new MacAddress(UNKNOWN_DMAC)) + .setLogicalSwitchRef(lsRef).setLocatorSet(locators).build(); + return remoteUcastMacs; + } + + private WriteTransaction putRemoteMcastMac(WriteTransaction transaction, String elanName, L2GatewayDevice device, IpAddress internalTunnelIp) { + Optional optionalNode = getNode(broker, device.getHwvtepNodeId()); + Node dstNode = optionalNode.get(); + if (dstNode == null) { + logger.debug("could not get device node {} ", device.getHwvtepNodeId()); + return null; + } + RemoteMcastMacs macs = createRemoteMcastMac(dstNode, elanName, internalTunnelIp); + HwvtepUtils.putRemoteMcastMac(transaction, dstNode.getNodeId(), macs); + return transaction; + } + + private void installRemoteMcastMac(BigInteger designatedDpnId, L2GatewayDevice device, String elanInstanceName) { + String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(designatedDpnId), device.getHwvtepNodeId()); + IpAddress internalTunnelIp = null; + if (tunnelInterfaceName != null) { + Interface tunnelInterface = DhcpServiceUtils.getInterfaceFromConfigDS(tunnelInterfaceName, broker); + if (tunnelInterface == null) { + logger.debug("Tunnel Interface is not present {}", tunnelInterfaceName); + return; + } + internalTunnelIp = tunnelInterface.getAugmentation(IfTunnel.class).getTunnelSource(); + WriteTransaction transaction = broker.newWriteOnlyTransaction(); + putRemoteMcastMac(transaction, elanInstanceName, device, internalTunnelIp); + if (transaction != null) { + transaction.submit(); + } + } + } + + private L2GatewayDevice getDeviceFromTunnelIp(String elanInstanceName, IpAddress tunnelIp) { + ConcurrentMap devices = L2GatewayCacheUtils.getCache(); + for (L2GatewayDevice device : devices.values()) { + if (device.getTunnelIp().equals(tunnelIp)) { + return device; + } + } + return null; + } + + private boolean isTunnelUp(String nodeName, BigInteger dpn) { + boolean isTunnelUp = false; + String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), nodeName); + if (tunnelInterfaceName == null) { + logger.debug("Tunnel Interface is not present {}", tunnelInterfaceName); + return isTunnelUp; + } + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface tunnelInterface = DhcpServiceUtils.getInterfaceFromOperationalDS(tunnelInterfaceName, broker); + if (tunnelInterface == null) { + logger.debug("Interface {} is not present in interface state", tunnelInterfaceName); + return isTunnelUp; + } + isTunnelUp = (tunnelInterface.getOperStatus() == OperStatus.Up) ? true :false; + return isTunnelUp; + } + + public void handleTunnelStateUp(IpAddress tunnelIp, BigInteger interfaceDpn) { + logger.trace("In handleTunnelStateUp tunnelIp {}, interfaceDpn {}", tunnelIp, interfaceDpn); + try { + List> tunnelIpElanPair = designatedDpnsToTunnelIpElanNameCache.get(DHCPMConstants.INVALID_DPID); + List dpns = DhcpServiceUtils.getListOfDpns(broker); + if (tunnelIpElanPair == null || tunnelIpElanPair.isEmpty()) { + return; + } + for (Pair pair : tunnelIpElanPair) { + if (tunnelIp.equals(pair.getLeft())) { + designateDpnId(tunnelIp, pair.getRight(), dpns); + } + } + } catch (Exception e) { + logger.error("Error in handleTunnelStateUp {}", e.getMessage()); + logger.trace("Exception details {}", e); + } + } + + private boolean isTunnelConfigured(BigInteger dpn, String hwVtepNodeId) { + String tunnelInterfaceName = getExternalTunnelInterfaceName(String.valueOf(dpn), hwVtepNodeId); + if (tunnelInterfaceName == null) { + return false; + } + Interface tunnelInterface = DhcpServiceUtils.getInterfaceFromConfigDS(tunnelInterfaceName, broker); + if (tunnelInterface == null) { + logger.debug("Tunnel Interface is not present {}", tunnelInterfaceName); + return false; + } + return true; + } + + public void unInstallDhcpFlowsForVms(String elanInstanceName, IpAddress tunnelIp, List dpns) { + Pair tunnelIpElanNamePair = new ImmutablePair(tunnelIp, elanInstanceName); + Set vmMacs = tunnelIpElanNameToVmMacCache.get(tunnelIpElanNamePair); + logger.trace("In unInstallFlowsForVms elanInstanceName {}, tunnelIp {}, dpns {}, vmMacs {}", elanInstanceName, tunnelIp, dpns, vmMacs); + if (vmMacs == null) { + return; + } + for (String vmMacAddress : vmMacs) { + for (BigInteger dpn : dpns) { + unInstallDhcpEntries(dpn, vmMacAddress, entityOwnershipService); + } + } + tunnelIpElanNameToVmMacCache.remove(tunnelIpElanNamePair); + } +} diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpInterfaceConfigListener.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpInterfaceConfigListener.java new file mode 100644 index 00000000..1bb1dd7b --- /dev/null +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpInterfaceConfigListener.java @@ -0,0 +1,87 @@ +/* + * 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.dhcpservice; + +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.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener; +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.opendaylight.vpnservice.interfacemgr.rev150331.IfTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.ParentRefs; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DhcpInterfaceConfigListener extends AbstractDataChangeListener implements AutoCloseable { + + private static final Logger logger = LoggerFactory.getLogger(DhcpInterfaceConfigListener.class); + private ListenerRegistration listenerRegistration; + private DataBroker dataBroker; + private DhcpExternalTunnelManager dhcpExternalTunnelManager; + + public DhcpInterfaceConfigListener(DataBroker dataBroker, DhcpExternalTunnelManager dhcpExternalTunnelManager) { + super(Interface.class); + this.dataBroker = dataBroker; + this.dhcpExternalTunnelManager = dhcpExternalTunnelManager; + registerListener(); + } + + private void registerListener() { + try { + listenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, + getWildCardPath(), DhcpInterfaceConfigListener.this, DataChangeScope.SUBTREE); + } catch (final Exception e) { + logger.error("DhcpInterfaceEventListener DataChange listener registration fail!", e); + throw new IllegalStateException("DhcpInterfaceEventListener registration Listener failed.", e); + } + } + + private InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(Interfaces.class).child(Interface.class); + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + logger.error("Error when cleaning up DataChangeListener.", e); + } + listenerRegistration = null; + } + logger.info("DhcpInterfaceConfigListener Closed"); + } + @Override + protected void remove(InstanceIdentifier identifier, Interface del) { + IfTunnel tunnelInterface = del.getAugmentation(IfTunnel.class); + if (tunnelInterface != null && !tunnelInterface.isInternal()) { + IpAddress tunnelIp = tunnelInterface.getTunnelDestination(); + ParentRefs interfce = del.getAugmentation(ParentRefs.class); + if (interfce != null) { + dhcpExternalTunnelManager.handleTunnelStateDown(tunnelIp, interfce.getDatapathNodeIdentifier()); + } + } + } + + @Override + protected void update(InstanceIdentifier identifier, Interface original, Interface update) { + // Handled in update () DhcpInterfaceEventListener + } + + @Override + protected void add(InstanceIdentifier identifier, Interface add) { + // Handled in add() DhcpInterfaceEventListener + } +} \ No newline at end of file diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpInterfaceEventListener.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpInterfaceEventListener.java index 5cc24565..199db913 100644 --- a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpInterfaceEventListener.java +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpInterfaceEventListener.java @@ -10,21 +10,28 @@ package org.opendaylight.vpnservice.dhcpservice; import java.math.BigInteger; import java.util.List; +import org.apache.commons.lang3.tuple.ImmutablePair; 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.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.dhcpservice.api.DHCPMConstants; import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener; import org.opendaylight.vpnservice.mdsalutil.MDSALDataStoreUtils; import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel; +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.InterfacesState; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.InterfaceNameMacAddresses; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710._interface.name.mac.addresses.InterfaceNameMacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710._interface.name.mac.addresses.InterfaceNameMacAddressBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710._interface.name.mac.addresses.InterfaceNameMacAddressKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.IfTunnel; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; @@ -40,26 +47,30 @@ public class DhcpInterfaceEventListener extends AbstractDataChangeListener listenerRegistration; private DataBroker dataBroker; private static final FutureCallback DEFAULT_CALLBACK = new FutureCallback() { + @Override public void onSuccess(Void result) { logger.debug("Success in Datastore write operation"); } + @Override public void onFailure(Throwable error) { logger.error("Error in Datastore write operation", error); } }; + private DhcpExternalTunnelManager dhcpExternalTunnelManager; - public DhcpInterfaceEventListener(DhcpManager dhcpManager, DataBroker dataBroker) { + public DhcpInterfaceEventListener(DhcpManager dhcpManager, DataBroker dataBroker, DhcpExternalTunnelManager dhcpExternalTunnelManager) { super(Interface.class); this.dhcpManager = dhcpManager; this.dataBroker = dataBroker; + this.dhcpExternalTunnelManager = dhcpExternalTunnelManager; registerListener(); } private void registerListener() { try { listenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, - getWildCardPath(), DhcpInterfaceEventListener.this, DataChangeScope.SUBTREE); + getWildCardPath(), DhcpInterfaceEventListener.this, DataChangeScope.SUBTREE); } catch (final Exception e) { logger.error("DhcpInterfaceEventListener DataChange listener registration fail!", e); throw new IllegalStateException("DhcpInterfaceEventListener registration Listener failed.", e); @@ -85,29 +96,109 @@ public class DhcpInterfaceEventListener extends AbstractDataChangeListener identifier, - Interface del) { - String interfaceName = del.getName(); + protected void remove(InstanceIdentifier identifier, Interface del) { List ofportIds = del.getLowerLayerIf(); + if (ofportIds == null || ofportIds.isEmpty()) { + return; + } NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0)); BigInteger dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId)); + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface iface = + DhcpServiceUtils.getInterfaceFromConfigDS(del.getName(), dataBroker); + if (iface != null) { + IfTunnel tunnelInterface = iface.getAugmentation(IfTunnel.class); + if (tunnelInterface != null && !tunnelInterface.isInternal()) { + IpAddress tunnelIp = tunnelInterface.getTunnelDestination(); + List dpns = DhcpServiceUtils.getListOfDpns(dataBroker); + if (dpns.contains(dpId)) { + dhcpExternalTunnelManager.handleTunnelStateDown(tunnelIp, dpId); + } + return; + } + } + String interfaceName = del.getName(); logger.trace("Received remove DCN for interface {} dpId {}", interfaceName, dpId); unInstallDhcpEntries(interfaceName, dpId); + dhcpManager.removeInterfaceCache(interfaceName); } @Override protected void update(InstanceIdentifier identifier, Interface original, Interface update) { + List ofportIds = update.getLowerLayerIf(); + if (ofportIds == null || ofportIds.isEmpty()) { + return; + } + NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0)); + BigInteger dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId)); + String interfaceName = update.getName(); + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface iface = + DhcpServiceUtils.getInterfaceFromConfigDS(interfaceName, dataBroker); + if (iface == null) { + logger.trace("Interface {} is not present in the config DS", interfaceName); + return; + } + if (update.getType() == null) { + logger.trace("Interface type for interface {} is null", interfaceName); + return; + } + if ((original.getOperStatus().getIntValue() ^ update.getOperStatus().getIntValue()) == 0) { + logger.trace("Interface operstatus {} is same", update.getOperStatus()); + return; + } + if (Tunnel.class.equals(update.getType())) { + IfTunnel tunnelInterface = iface.getAugmentation(IfTunnel.class); + if (tunnelInterface != null && !tunnelInterface.isInternal()) { + IpAddress tunnelIp = tunnelInterface.getTunnelDestination(); + List dpns = DhcpServiceUtils.getListOfDpns(dataBroker); + if (dpns.contains(dpId)) { + if (update.getOperStatus() == OperStatus.Down) { + dhcpExternalTunnelManager.handleTunnelStateDown(tunnelIp, dpId); + } else if (update.getOperStatus() == OperStatus.Up) { + dhcpExternalTunnelManager.handleTunnelStateUp(tunnelIp, dpId); + } + } + } + return; + } + if (update.getOperStatus() == OperStatus.Down) { + unInstallDhcpEntries(interfaceName, dpId); + dhcpManager.removeInterfaceCache(interfaceName); + } else if (update.getOperStatus() == OperStatus.Up) { + if (!dpId.equals(DHCPMConstants.INVALID_DPID)) { + installDhcpEntries(interfaceName, dpId); + dhcpManager.updateInterfaceCache(interfaceName, new ImmutablePair(dpId, update.getPhysAddress().getValue())); + } + } } @Override protected void add(InstanceIdentifier identifier, Interface add) { String interfaceName = add.getName(); List ofportIds = add.getLowerLayerIf(); + if (ofportIds == null || ofportIds.isEmpty()) { + return; + } NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0)); BigInteger dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId)); logger.trace("Received add DCN for interface {}, dpid {}", interfaceName, dpId); - installDhcpEntries(interfaceName, dpId); + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface iface = + DhcpServiceUtils.getInterfaceFromConfigDS(add.getName(), dataBroker); + if (iface != null) { + IfTunnel tunnelInterface = iface.getAugmentation(IfTunnel.class); + if (tunnelInterface != null && !tunnelInterface.isInternal()) { + IpAddress tunnelIp = tunnelInterface.getTunnelDestination(); + List dpns = DhcpServiceUtils.getListOfDpns(dataBroker); + if (dpns.contains(dpId)) { + dhcpExternalTunnelManager.handleTunnelStateUp(tunnelIp, dpId); + } + return; + } + } + if (!dpId.equals(DHCPMConstants.INVALID_DPID)) { + installDhcpEntries(interfaceName, dpId); + dhcpManager.updateInterfaceCache(interfaceName, new ImmutablePair(dpId, add.getPhysAddress().getValue())); + } } private String getNeutronMacAddress(String interfaceName) { @@ -116,7 +207,6 @@ public class DhcpInterfaceEventListener extends AbstractDataChangeListener implements AutoCloseable { + + private static final Logger logger = LoggerFactory.getLogger(DhcpInterfaceEventListener.class); + private ListenerRegistration listenerRegistration; + private final DhcpExternalTunnelManager dhcpExternalTunnelManager; + private final DataBroker dataBroker; + + public DhcpL2GatewayConnectionListener(DataBroker dataBroker, DhcpExternalTunnelManager dhcpExternalTunnelManager) { + super(L2gatewayConnection.class, DhcpL2GatewayConnectionListener.class); + this.dhcpExternalTunnelManager = dhcpExternalTunnelManager; + this.dataBroker = dataBroker; + } + + @Override + protected void remove(InstanceIdentifier identifier, + L2gatewayConnection del) { + Uuid gatewayId = del.getL2gatewayId(); + InstanceIdentifier inst = InstanceIdentifier.create(Neutron.class).child(L2gateways.class) + .child(L2gateway.class, new L2gatewayKey(gatewayId)); + Optional l2Gateway = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, inst); + if (!l2Gateway.isPresent()) { + return; + } + Uuid networkUuid = del.getNetworkId(); + boolean isLastConnection = true; + InstanceIdentifier l2gatewayConnectionIdentifier = InstanceIdentifier.create(Neutron.class).child(L2gatewayConnections.class); + Optional l2GwConnection = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, l2gatewayConnectionIdentifier); + List l2GatewayConnectionList = l2GwConnection.get().getL2gatewayConnection(); + for (L2gatewayConnection l2gatewayConnection : l2GatewayConnectionList) { + if (networkUuid.equals(l2gatewayConnection.getNetworkId())) { + isLastConnection = false; + break; + } + } + if (!isLastConnection) { + return; + } + List l2Devices = l2Gateway.get().getDevices(); + for (Devices l2Device : l2Devices) { + String l2DeviceName = l2Device.getDeviceName(); + L2GatewayDevice l2GatewayDevice = L2GatewayCacheUtils.getL2DeviceFromCache(l2DeviceName); + IpAddress tunnelIp = l2GatewayDevice.getTunnelIp(); + BigInteger designatedDpnId = dhcpExternalTunnelManager.readDesignatedSwitchesForExternalTunnel(tunnelIp, del.getNetworkId().getValue()); + if (designatedDpnId == null || designatedDpnId.equals(DHCPMConstants.INVALID_DPID)) { + logger.error("Could not find desiganted DPN ID"); + return; + } + dhcpExternalTunnelManager.removeDesignatedSwitchForExternalTunnel(designatedDpnId, tunnelIp, del.getNetworkId().getValue()); + } + } + + @Override + protected void update(InstanceIdentifier identifier, + L2gatewayConnection original, L2gatewayConnection update) { + + } + + @Override + protected void add(InstanceIdentifier identifier, + L2gatewayConnection add) { + + } + + @Override + protected InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(Neutron.class).child(L2gatewayConnections.class) + .child(L2gatewayConnection.class); + } + + @Override + protected ClusteredDataChangeListener getDataChangeListener() { + return DhcpL2GatewayConnectionListener.this; + } + + @Override + protected DataChangeScope getDataChangeScope() { + return AsyncDataBroker.DataChangeScope.SUBTREE; + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + logger.error("Error when cleaning up DataChangeListener.", e); + } + listenerRegistration = null; + } + logger.info("DhcpL2GatewayConnection listener Closed"); + } +} diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpLogicalSwitchListener.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpLogicalSwitchListener.java new file mode 100644 index 00000000..5f466116 --- /dev/null +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpLogicalSwitchListener.java @@ -0,0 +1,150 @@ +/* + * 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.dhcpservice; + +import java.math.BigInteger; +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.DataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.dhcpservice.api.DHCPMConstants; +import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener; +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.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation; +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.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.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DhcpLogicalSwitchListener extends AbstractDataChangeListener implements AutoCloseable { + + private static final Logger logger = LoggerFactory.getLogger(DhcpLogicalSwitchListener.class); + private DhcpExternalTunnelManager dhcpExternalTunnelManager; + private ListenerRegistration listenerRegistration; + private DataBroker dataBroker; + + public DhcpLogicalSwitchListener(DhcpExternalTunnelManager dhcpManager, DataBroker dataBroker) { + super(LogicalSwitches.class); + this.dhcpExternalTunnelManager = dhcpManager; + this.dataBroker = dataBroker; + registerListener(); + } + + private void registerListener() { + try { + listenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, + getWildCardPath(), DhcpLogicalSwitchListener.this, DataChangeScope.SUBTREE); + } catch (final Exception e) { + logger.error("DhcpLogicalSwitchListener DataChange listener registration fail!", e); + throw new IllegalStateException("DhcpLogicalSwitchListener registration Listener failed.", e); + } + } + + private InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(NetworkTopology.class) + .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID)) + .child(Node.class).augmentation(HwvtepGlobalAugmentation.class) + .child(LogicalSwitches.class); + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + logger.error("Error when cleaning up DataChangeListener.", e); + } + listenerRegistration = null; + } + logger.info("DhcpLogicalSwitchListener Closed"); + } + + @Override + protected void remove(InstanceIdentifier identifier, + LogicalSwitches del) { + logger.trace("Received LogicalSwitch remove DCN"); + String elanInstanceName = del.getLogicalSwitchUuid().toString(); + ConcurrentMap devices = L2GatewayCacheUtils.getCache(); + String nodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue(); + L2GatewayDevice targetDevice = null; + for (L2GatewayDevice device : devices.values()) { + if (nodeId.equals(device.getHwvtepNodeId())) { + targetDevice = device; + break; + } + } + if (targetDevice == null) { + logger.error("Logical Switch Device with name {} is not present in L2GW cache", elanInstanceName); + return; + } + IpAddress tunnelIp = targetDevice.getTunnelIp(); + handleLogicalSwitchRemove(elanInstanceName, tunnelIp); + } + + @Override + protected void update(InstanceIdentifier identifier, + LogicalSwitches original, LogicalSwitches update) { + logger.trace("Received LogicalSwitch update DCN"); + + } + + @Override + protected void add(InstanceIdentifier identifier, + LogicalSwitches add) { + logger.trace("Received LogicalSwitch add DCN"); + String elanInstanceName = add.getHwvtepNodeName().getValue(); + ConcurrentMap devices = L2GatewayCacheUtils.getCache(); + String nodeId = identifier.firstKeyOf(Node.class).getNodeId().getValue(); + L2GatewayDevice targetDevice = null; + for (L2GatewayDevice device : devices.values()) { + if (nodeId.equals(device.getHwvtepNodeId())) { + targetDevice = device; + break; + } + } + if (targetDevice == null) { + logger.error("Logical Switch Device with name {} is not present in L2GW cache", elanInstanceName); + return; + } + IpAddress tunnelIp = targetDevice.getTunnelIp(); + handleLogicalSwitchAdd(elanInstanceName, tunnelIp); + + } + + private void handleLogicalSwitchRemove(String elanInstanceName, IpAddress tunnelIp) { + BigInteger designatedDpnId; + designatedDpnId = dhcpExternalTunnelManager.readDesignatedSwitchesForExternalTunnel(tunnelIp, elanInstanceName); + if (designatedDpnId == null || designatedDpnId.equals(DHCPMConstants.INVALID_DPID)) { + logger.error("Could not find designated DPN ID"); + return; + } + dhcpExternalTunnelManager.removeDesignatedSwitchForExternalTunnel(designatedDpnId, tunnelIp, elanInstanceName); + } + + private void handleLogicalSwitchAdd(String elanInstanceName, IpAddress tunnelIp) { + List dpns = DhcpServiceUtils.getListOfDpns(dataBroker); + BigInteger designatedDpnId; + designatedDpnId = dhcpExternalTunnelManager.designateDpnId(tunnelIp, elanInstanceName, dpns); + if (designatedDpnId == null || designatedDpnId.equals(DHCPMConstants.INVALID_DPID)) { + logger.error("Unable to designate a DPN"); + return; + } + } +} \ No newline at end of file diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpManager.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpManager.java index 2309defb..af36ca95 100644 --- a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpManager.java +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpManager.java @@ -7,36 +7,22 @@ */ package org.opendaylight.vpnservice.dhcpservice; -import org.opendaylight.vpnservice.neutronvpn.interfaces.INeutronVpnManager; -import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey; -import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets; - -import com.google.common.base.Optional; - -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron; -import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - import java.math.BigInteger; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import com.google.common.util.concurrent.FutureCallback; - +import org.apache.commons.lang3.tuple.ImmutablePair; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.vpnservice.dhcpservice.api.DHCPMConstants; -import org.opendaylight.vpnservice.mdsalutil.ActionInfo; -import org.opendaylight.vpnservice.mdsalutil.ActionType; 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.MatchFieldType; import org.opendaylight.vpnservice.mdsalutil.MatchInfo; import org.opendaylight.vpnservice.mdsalutil.NwConstants; import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; -import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols; +import org.opendaylight.vpnservice.neutronvpn.interfaces.INeutronVpnManager; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet; import org.slf4j.Logger; @@ -53,16 +39,8 @@ public class DhcpManager implements AutoCloseable { private int dhcpOptRebindingTime = 0; private String dhcpOptDefDomainName; private INeutronVpnManager neutronVpnService; - - private static final FutureCallback DEFAULT_CALLBACK = - new FutureCallback() { - public void onSuccess(Void result) { - logger.debug("Success in Datastore write operation"); - } - public void onFailure(Throwable error) { - logger.error("Error in Datastore write operation", error); - }; - }; + // cache used to maintain DpnId and physical address for each interface. + private static HashMap> interfaceToDpnIdMacAddress = new HashMap>(); /** * @param db - dataBroker reference @@ -86,43 +64,6 @@ public class DhcpManager implements AutoCloseable { logger.info("DHCP Manager Closed"); } - public void installDhcpEntries(BigInteger dpnId) { - logger.debug("Installing Default DHCP Flow tp DPN: {}", dpnId); - setupDefaultDhcpFlow(dpnId, NwConstants.DHCP_TABLE, NwConstants.ADD_FLOW); - } - - private void setupDefaultDhcpFlow(BigInteger dpId, short tableId, int addOrRemove) { - - List matches = new ArrayList(); - - matches.add(new MatchInfo(MatchFieldType.eth_type, - new long[] { NwConstants.ETHTYPE_IPV4 })); - matches.add(new MatchInfo(MatchFieldType.ip_proto, - new long[] { IPProtocols.UDP.intValue() })); - matches.add(new MatchInfo(MatchFieldType.udp_src, - new long[] { DHCPMConstants.dhcpClientPort })); - matches.add(new MatchInfo(MatchFieldType.udp_dst, - new long[] { DHCPMConstants.dhcpServerPort })); - - List instructions = new ArrayList(); - List actionsInfos = new ArrayList(); - - // Punt to controller - actionsInfos.add(new ActionInfo(ActionType.punt_to_controller, - new String[] {})); - instructions.add(new InstructionInfo(InstructionType.write_actions, - actionsInfos)); - FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, - getDefaultDhcpFlowRef(dpId, tableId),DHCPMConstants.DEFAULT_DHCP_FLOW_PRIORITY, "DHCP", 0, 0, - DHCPMConstants.COOKIE_DHCP_BASE, matches, instructions); - mdsalUtil.installFlow(flowEntity); - } - - private String getDefaultDhcpFlowRef(BigInteger dpId, long tableId) { - return new StringBuffer().append(DHCPMConstants.FLOWID_PREFIX).append(dpId) - .append(NwConstants.FLOWID_SEPARATOR).append(tableId).toString(); - } - public int setLeaseDuration(int leaseDuration) { configureLeaseDuration(leaseDuration); return getDhcpLeaseTime(); @@ -174,6 +115,8 @@ public class DhcpManager implements AutoCloseable { public Port getNeutronPort(String name) { try { return neutronVpnService.getNeutronPort(name); + } catch (IllegalArgumentException e) { + return null; } catch (Exception ex) { logger.trace("In getNeutronPort interface name passed {} exception message {}.", name, ex.getMessage()); return null; @@ -181,59 +124,11 @@ public class DhcpManager implements AutoCloseable { } public void installDhcpEntries(BigInteger dpnId, String vmMacAddress) { - setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE, vmMacAddress, NwConstants.ADD_FLOW); - } - - private void setupDhcpFlowEntry(BigInteger dpId, short tableId, String vmMacAddress, int addOrRemove) { - if (dpId == null || dpId == DHCPMConstants.INVALID_DPID || vmMacAddress == null) { - return; - } - List matches = new ArrayList(); - - matches.add(new MatchInfo(MatchFieldType.eth_type, - new long[] { NwConstants.ETHTYPE_IPV4 })); - matches.add(new MatchInfo(MatchFieldType.ip_proto, - new long[] { IPProtocols.UDP.intValue() })); - matches.add(new MatchInfo(MatchFieldType.udp_src, - new long[] { DHCPMConstants.dhcpClientPort })); - matches.add(new MatchInfo(MatchFieldType.udp_dst, - new long[] { DHCPMConstants.dhcpServerPort })); - matches.add(new MatchInfo(MatchFieldType.eth_src, - new String[] { vmMacAddress })); - - List instructions = new ArrayList(); - List actionsInfos = new ArrayList(); - - // Punt to controller - actionsInfos.add(new ActionInfo(ActionType.punt_to_controller, - new String[] {})); - instructions.add(new InstructionInfo(InstructionType.write_actions, - actionsInfos)); - if (addOrRemove == NwConstants.DEL_FLOW) { - FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, - getDhcpFlowRef(dpId, tableId, vmMacAddress), - DHCPMConstants.DEFAULT_DHCP_FLOW_PRIORITY, "DHCP", 0, 0, - DHCPMConstants.COOKIE_DHCP_BASE, matches, null); - logger.trace("Removing DHCP Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress); - mdsalUtil.removeFlow(flowEntity); - } else { - FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, - getDhcpFlowRef(dpId, tableId, vmMacAddress),DHCPMConstants.DEFAULT_DHCP_FLOW_PRIORITY, "DHCP", 0, 0, - DHCPMConstants.COOKIE_DHCP_BASE, matches, instructions); - logger.trace("Installing DHCP Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress); - mdsalUtil.installFlow(flowEntity); - } - } - - private String getDhcpFlowRef(BigInteger dpId, long tableId, String vmMacAddress) { - return new StringBuffer().append(DHCPMConstants.FLOWID_PREFIX) - .append(dpId).append(NwConstants.FLOWID_SEPARATOR) - .append(tableId).append(NwConstants.FLOWID_SEPARATOR) - .append(vmMacAddress).toString(); + DhcpServiceUtils.setupDhcpFlowEntry(dpnId, NwConstants.DHCP_TABLE, vmMacAddress, NwConstants.ADD_FLOW, mdsalUtil); } public void unInstallDhcpEntries(BigInteger dpId, String vmMacAddress) { - setupDhcpFlowEntry(dpId, NwConstants.DHCP_TABLE, vmMacAddress, NwConstants.DEL_FLOW); + DhcpServiceUtils.setupDhcpFlowEntry(dpId, NwConstants.DHCP_TABLE, vmMacAddress, NwConstants.DEL_FLOW, mdsalUtil); } public void setupTableMissForDhcpTable(BigInteger dpId) { @@ -245,5 +140,29 @@ public class DhcpManager implements AutoCloseable { 0, "DHCP Table Miss Flow", 0, 0, DHCPMConstants.COOKIE_DHCP_BASE, matches, instructions); mdsalUtil.installFlow(flowEntity); + setupTableMissForHandlingExternalTunnel(dpId); + } + + private void setupTableMissForHandlingExternalTunnel(BigInteger dpId) { + List matches = new ArrayList(); + List instructions = new ArrayList(); + instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.EXTERNAL_TUNNEL_TABLE })); + + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.DHCP_TABLE_EXTERNAL_TUNNEL, "DHCPTableMissFlowForExternalTunnel", + 0, "DHCP Table Miss Flow For External Tunnel", 0, 0, + DHCPMConstants.COOKIE_DHCP_BASE, matches, instructions); + mdsalUtil.installFlow(flowEntity); + } + + public void updateInterfaceCache(String interfaceName, ImmutablePair pair) { + interfaceToDpnIdMacAddress.put(interfaceName, pair); + } + + public ImmutablePair getInterfaceCache(String interfaceName) { + return interfaceToDpnIdMacAddress.get(interfaceName); + } + + public void removeInterfaceCache(String interfaceName) { + interfaceToDpnIdMacAddress.remove(interfaceName); } } diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpNeutronPortListener.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpNeutronPortListener.java new file mode 100644 index 00000000..385d9192 --- /dev/null +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpNeutronPortListener.java @@ -0,0 +1,109 @@ +/* + * 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.dhcpservice; + +import java.math.BigInteger; +import java.util.List; + +import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener; +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.AsyncDataBroker.DataChangeScope; +import org.opendaylight.vpnservice.datastoreutils.AsyncClusteredDataChangeListenerBase; +import org.opendaylight.vpnservice.neutronvpn.api.utils.NeutronUtils; +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.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.rev150712.Neutron; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DhcpNeutronPortListener extends AsyncClusteredDataChangeListenerBase implements AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(DhcpNeutronPortListener.class); + + private ListenerRegistration listenerRegistration; + private DhcpExternalTunnelManager dhcpExternalTunnelManager; + private DataBroker broker; + + public DhcpNeutronPortListener(final DataBroker db, final DhcpExternalTunnelManager dhcpExternalTunnelManager) { + super(Port.class, DhcpNeutronPortListener.class); + this.dhcpExternalTunnelManager = dhcpExternalTunnelManager; + this.broker = db; + } + + @Override + protected InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class); + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + LOG.error("Error when cleaning up DhcpNeutronPortListener.", e); + } + listenerRegistration = null; + } + LOG.debug("DhcpNeutronPortListener Listener Closed"); + } + + @Override + protected void remove(InstanceIdentifier identifier, Port del) { + LOG.trace("Port removed: {}", del); + if(NeutronUtils.isPortVnicTypeNormal(del)) { + return; + } + String macAddress = del.getMacAddress(); + Uuid networkId = del.getNetworkId(); + String segmentationId = DhcpServiceUtils.getSegmentationId(networkId, broker); + if (segmentationId == null) { + return; + } + List listOfDpns = DhcpServiceUtils.getListOfDpns(broker); + dhcpExternalTunnelManager.unInstallDhcpFlowsForVms(networkId.getValue(), listOfDpns, macAddress); + dhcpExternalTunnelManager.removeVniMacToPortCache(new BigInteger(segmentationId), macAddress); + } + + @Override + protected void update(InstanceIdentifier identifier, Port original, Port update) { + LOG.trace("Port changed to {}", update); + } + + @Override + protected void add(InstanceIdentifier identifier, Port add) { + LOG.trace("Port added {}", add); + if(NeutronUtils.isPortVnicTypeNormal(add)) { + LOG.trace("Port is normal {}", add); + return; + } + String macAddress = add.getMacAddress(); + Uuid networkId = add.getNetworkId(); + String segmentationId = DhcpServiceUtils.getSegmentationId(networkId, broker); + if (segmentationId == null) { + LOG.trace("segmentation id is null"); + return; + } + dhcpExternalTunnelManager.updateVniMacToPortCache(new BigInteger(segmentationId), macAddress, add); + } + + @Override + protected ClusteredDataChangeListener getDataChangeListener() { + return DhcpNeutronPortListener.this; + } + + @Override + protected DataChangeScope getDataChangeScope() { + return AsyncDataBroker.DataChangeScope.SUBTREE; + } +} diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpPktHandler.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpPktHandler.java index 60d8bd72..c74171a3 100644 --- a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpPktHandler.java +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpPktHandler.java @@ -13,7 +13,6 @@ import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutionException; @@ -72,11 +71,12 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { private final DataBroker dataBroker; private final DhcpManager dhcpMgr; private OdlInterfaceRpcService interfaceManagerRpc; - private static HashMap> localCache = new HashMap>(); private boolean computeUdpChecksum = true; private PacketProcessingService pktService; + private DhcpExternalTunnelManager dhcpExternalTunnelManager; - public DhcpPktHandler(final DataBroker broker, final DhcpManager dhcpManager) { + public DhcpPktHandler(final DataBroker broker, final DhcpManager dhcpManager, final DhcpExternalTunnelManager dhcpExternalTunnelManager) { + this.dhcpExternalTunnelManager = dhcpExternalTunnelManager; this.dataBroker = broker; dhcpMgr = dhcpManager; } @@ -84,16 +84,15 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { //TODO: Handle this in a separate thread @Override public void onPacketReceived(PacketReceived packet) { - LOG.trace("Pkt received: {}", packet); Class pktInReason = packet.getPacketInReason(); - short tableId = packet.getTableId().getValue(); if (isPktInReasonSendtoCtrl(pktInReason)) { byte[] inPayload = packet.getPayload(); Ethernet ethPkt = new Ethernet(); try { ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NumBitsInAByte); } catch (Exception e) { - LOG.warn("Failed to decode DHCP Packet", e); + LOG.warn("Failed to decode DHCP Packet {}", e); + LOG.trace("Received packet {}", packet); return; } try { @@ -101,29 +100,33 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { pktIn = getDhcpPktIn(ethPkt); if (pktIn != null) { LOG.trace("DHCPPkt received: {}", pktIn); + LOG.trace("Received Packet: {}", packet); BigInteger metadata = packet.getMatch().getMetadata().getMetadata(); long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue(); + String macAddress = DHCPUtils.byteArrayToString(ethPkt.getSourceMACAddress()); + BigInteger tunnelId = packet.getMatch().getTunnel() == null ? null : packet.getMatch().getTunnel().getTunnelId(); String interfaceName = getInterfaceNameFromTag(portTag); ImmutablePair pair = getDpnIdPhysicalAddressFromInterfaceName(interfaceName); - DHCP replyPkt = handleDhcpPacket(pktIn, interfaceName); + DHCP replyPkt = handleDhcpPacket(pktIn, interfaceName, macAddress, tunnelId); byte[] pktOut = getDhcpPacketOut(replyPkt, ethPkt, pair.getRight()); - sendPacketOut(pktOut, pair.getLeft(), interfaceName); + sendPacketOut(pktOut, pair.getLeft(), interfaceName, tunnelId); } } catch (Exception e) { - LOG.warn("Failed to get DHCP Reply {}", e); + LOG.warn("Failed to get DHCP Reply"); + LOG.trace("Reason for failure {}", e); } } } - private void sendPacketOut(byte[] pktOut, BigInteger dpnId, String interfaceName) { + private void sendPacketOut(byte[] pktOut, BigInteger dpnId, String interfaceName, BigInteger tunnelId) { LOG.trace("Sending packet out DpId {}, portId {}, vlanId {}, interfaceName {}", dpnId, interfaceName); - List action = getEgressAction(interfaceName); + List action = getEgressAction(interfaceName, tunnelId); TransmitPacketInput output = MDSALUtil.getPacketOut(action, pktOut, dpnId); LOG.trace("Transmitting packet: {}",output); this.pktService.transmitPacket(output); } - private DHCP handleDhcpPacket(DHCP dhcpPkt, String interfaceName) { + private DHCP handleDhcpPacket(DHCP dhcpPkt, String interfaceName, String macAddress, BigInteger tunnelId) { LOG.debug("DHCP pkt rcvd {}", dhcpPkt); byte msgType = dhcpPkt.getMsgType(); if (msgType == DHCPConstants.MSG_DECLINE) { @@ -133,8 +136,12 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { LOG.debug("DHCPRELEASE received"); return null; } - - Port nPort = getNeutronPort(interfaceName); + Port nPort; + if (tunnelId != null) { + nPort = dhcpExternalTunnelManager.readVniMacToPortCache(tunnelId, macAddress); + } else { + nPort = getNeutronPort(interfaceName); + } Subnet nSubnet = getNeutronSubnet(nPort); DhcpInfo dhcpInfo = getDhcpInfo(nPort, nSubnet); LOG.trace("NeutronPort: {} \n NeutronSubnet: {}, dhcpInfo{}",nPort, nSubnet, dhcpInfo); @@ -160,13 +167,6 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { dhcpInfo.setClientIp(clientIp).setServerIp(serverIp) .setCidr(nSubnet.getCidr()).setHostRoutes(nSubnet.getHostRoutes()) .setDnsServersIpAddrs(dnsServers).setGatewayIp(serverIp); - } else { - //FIXME: Delete this test code - LOG.error("TestOnly Code"); - dhcpInfo = new DhcpInfo(); - dhcpInfo.setClientIp("1.1.1.3").setServerIp("1.1.1.1") - .setCidr("1.1.1.0/24").addDnsServer("1.1.1.1"); - LOG.warn("Failed to get Subnet info for DHCP reply"); } return dhcpInfo; } @@ -181,7 +181,6 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { private DHCP getDhcpPktIn(Ethernet actualEthernetPacket) { Ethernet ethPkt = actualEthernetPacket; - LOG.trace("Inside getDhcpPktIn ethPkt {} \n getPayload {}", ethPkt, ethPkt.getPayload()); if (ethPkt.getEtherType() == (short)NwConstants.ETHTYPE_802_1Q) { ethPkt = (Ethernet)ethPkt.getPayload(); } @@ -197,7 +196,8 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { try { reply.deserialize(rawDhcpPayload, 0, rawDhcpPayload.length); } catch (PacketException e) { - LOG.warn("Failed to deserialize DHCP pkt {}", e); + LOG.warn("Failed to deserialize DHCP pkt"); + LOG.trace("Reason for failure {}", e); return null; } return reply; @@ -329,7 +329,6 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { ether.setEtherType(EtherTypes.IPv4.shortValue()); ether.setPayload(ip4Reply); } - //TODO: ether.setSourceMACAddress(getServerMacAddress(phyAddrees)); ether.setDestinationMACAddress(etherPkt.getSourceMACAddress()); @@ -577,12 +576,15 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { return id; } - private List getEgressAction(String interfaceName) { + private List getEgressAction(String interfaceName, BigInteger tunnelId) { List actions = null; try { + GetEgressActionsForInterfaceInputBuilder egressAction = new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName); + if (tunnelId != null) { + egressAction.setTunnelKey(tunnelId.longValue()); + } Future> result = - interfaceManagerRpc.getEgressActionsForInterface( - new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).build()); + interfaceManagerRpc.getEgressActionsForInterface(egressAction.build()); RpcResult rpcResult = result.get(); if(!rpcResult.isSuccessful()) { LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", interfaceName, rpcResult.getErrors()); @@ -596,7 +598,7 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { } private ImmutablePair getDpnIdPhysicalAddressFromInterfaceName(String interfaceName) { - ImmutablePair pair = localCache.get(interfaceName); + ImmutablePair pair = dhcpMgr.getInterfaceCache(interfaceName); if (pair!=null && pair.getLeft() != null && pair.getRight() != null) { return pair; } @@ -609,7 +611,7 @@ public class DhcpPktHandler implements AutoCloseable, PacketProcessingListener { BigInteger dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId)); String phyAddress = interfaceState==null ? "":interfaceState.getPhysAddress().getValue(); pair = new ImmutablePair(dpId, phyAddress); - localCache.put(interfaceName, pair); - return null; + dhcpMgr.updateInterfaceCache(interfaceName, pair); + return pair; } } diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpProvider.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpProvider.java index 439d7352..f86b4642 100644 --- a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpProvider.java +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpProvider.java @@ -7,15 +7,18 @@ */ package org.opendaylight.vpnservice.dhcpservice; -import org.opendaylight.vpnservice.neutronvpn.interfaces.INeutronVpnManager; -import org.opendaylight.controller.sal.binding.api.NotificationProviderService; -import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService; -import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService; -import org.opendaylight.yangtools.concepts.Registration; 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.controller.sal.binding.api.BindingAwareBroker.ProviderContext; import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; +import org.opendaylight.controller.sal.binding.api.NotificationProviderService; import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.vpnservice.neutronvpn.interfaces.INeutronVpnManager; +import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService; +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.yangtools.concepts.Registration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +35,15 @@ public class DhcpProvider implements BindingAwareProvider, AutoCloseable { private DhcpConfigListener dhcpConfigListener; private OdlInterfaceRpcService interfaceManagerRpc; private DhcpInterfaceEventListener dhcpInterfaceEventListener; + private DhcpExternalTunnelManager dhcpExternalTunnelManager; + private DhcpNeutronPortListener dhcpNeutronPortListener; + private DhcpLogicalSwitchListener dhcpLogicalSwitchListener; + private DhcpUCastMacListener dhcpUCastMacListener; + private ItmRpcService itmRpcService; + private DhcpInterfaceConfigListener dhcpInterfaceConfigListener; + private EntityOwnershipService entityOwnershipService; + private DhcpDesignatedDpnListener dhcpDesignatedDpnListener; + private DhcpL2GatewayConnectionListener dhcpL2GatewayConnectionListener; @Override public void onSessionInitiated(ProviderContext session) { @@ -42,19 +54,29 @@ public class DhcpProvider implements BindingAwareProvider, AutoCloseable { dhcpManager = new DhcpManager(dataBroker); dhcpManager.setMdsalManager(mdsalManager); dhcpManager.setNeutronVpnService(neutronVpnManager); - dhcpPktHandler = new DhcpPktHandler(dataBroker, dhcpManager); + dhcpExternalTunnelManager = new DhcpExternalTunnelManager(dataBroker, mdsalManager, itmRpcService, entityOwnershipService); + dhcpPktHandler = new DhcpPktHandler(dataBroker, dhcpManager, dhcpExternalTunnelManager); dhcpPktHandler.setPacketProcessingService(pktProcessingService); dhcpPktHandler.setInterfaceManagerRpc(interfaceManagerRpc); packetListener = notificationService.registerNotificationListener(dhcpPktHandler); - dhcpNodeListener = new NodeListener(dataBroker, dhcpManager); + dhcpNodeListener = new NodeListener(dataBroker, dhcpManager, dhcpExternalTunnelManager); dhcpConfigListener = new DhcpConfigListener(dataBroker, dhcpManager); - dhcpInterfaceEventListener = new DhcpInterfaceEventListener(dhcpManager, dataBroker); + dhcpInterfaceEventListener = new DhcpInterfaceEventListener(dhcpManager, dataBroker, dhcpExternalTunnelManager); + dhcpInterfaceConfigListener = new DhcpInterfaceConfigListener(dataBroker, dhcpExternalTunnelManager); + dhcpLogicalSwitchListener = new DhcpLogicalSwitchListener(dhcpExternalTunnelManager, dataBroker); + dhcpUCastMacListener = new DhcpUCastMacListener(dhcpExternalTunnelManager, dataBroker); + dhcpUCastMacListener.registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker); + dhcpNeutronPortListener = new DhcpNeutronPortListener(dataBroker, dhcpExternalTunnelManager); + dhcpNeutronPortListener.registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker); + dhcpDesignatedDpnListener = new DhcpDesignatedDpnListener(dhcpExternalTunnelManager, dataBroker); + dhcpDesignatedDpnListener.registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker); + dhcpL2GatewayConnectionListener = new DhcpL2GatewayConnectionListener(dataBroker, dhcpExternalTunnelManager); + dhcpL2GatewayConnectionListener.registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker); } catch (Exception e) { LOG.error("Error initializing services {}", e); } } - public void setMdsalManager(IMdsalApiManager mdsalManager) { this.mdsalManager = mdsalManager; } @@ -84,4 +106,12 @@ public class DhcpProvider implements BindingAwareProvider, AutoCloseable { public void setInterfaceManagerRpc(OdlInterfaceRpcService interfaceManagerRpc) { this.interfaceManagerRpc = interfaceManagerRpc; } + + public void setItmRpcService(ItmRpcService itmRpcService) { + this.itmRpcService = itmRpcService; + } + + public void setEntityOwnershipService(EntityOwnershipService entityOwnershipService) { + this.entityOwnershipService = entityOwnershipService; + } } diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpServiceUtils.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpServiceUtils.java new file mode 100644 index 00000000..c6fd5492 --- /dev/null +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpServiceUtils.java @@ -0,0 +1,225 @@ +/* + * 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.dhcpservice; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.vpnservice.dhcpservice.api.DHCPMConstants; +import org.opendaylight.vpnservice.mdsalutil.ActionInfo; +import org.opendaylight.vpnservice.mdsalutil.ActionType; +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.MatchFieldType; +import org.opendaylight.vpnservice.mdsalutil.MatchInfo; +import org.opendaylight.vpnservice.mdsalutil.NwConstants; +import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols; +import org.opendaylight.vpnservice.neutronvpn.api.utils.NeutronUtils; +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.InterfacesState; +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.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.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.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.rev150712.Neutron; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanDpnInterfaces; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; + +public class DhcpServiceUtils { + + private static final Logger logger = LoggerFactory.getLogger(DhcpServiceUtils.class); + + public static Interface getInterfaceFromConfigDS(String interfaceName, DataBroker dataBroker) { + InterfaceKey interfaceKey = new InterfaceKey(interfaceName); + InstanceIdentifier interfaceId = getInterfaceIdentifier(interfaceKey); + Optional interfaceOptional = MDSALUtil.read(LogicalDatastoreType.CONFIGURATION, interfaceId, dataBroker); + if (!interfaceOptional.isPresent()) { + return null; + } + return interfaceOptional.get(); + } + + private static InstanceIdentifier getInterfaceIdentifier(InterfaceKey interfaceKey) { + InstanceIdentifier.InstanceIdentifierBuilder interfaceInstanceIdentifierBuilder = + InstanceIdentifier.builder(Interfaces.class).child(Interface.class, interfaceKey); + return interfaceInstanceIdentifierBuilder.build(); + } + + public static void setupDhcpFlowEntry(BigInteger dpId, short tableId, String vmMacAddress, int addOrRemove, IMdsalApiManager mdsalUtil) { + if (dpId == null || dpId.equals(DHCPMConstants.INVALID_DPID) || vmMacAddress == null) { + return; + } + List matches = getDhcpMatch(vmMacAddress); + + List instructions = new ArrayList(); + List actionsInfos = new ArrayList(); + + // Punt to controller + actionsInfos.add(new ActionInfo(ActionType.punt_to_controller, + new String[] {})); + instructions.add(new InstructionInfo(InstructionType.write_actions, + actionsInfos)); + if (addOrRemove == NwConstants.DEL_FLOW) { + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, + getDhcpFlowRef(dpId, tableId, vmMacAddress), + DHCPMConstants.DEFAULT_DHCP_FLOW_PRIORITY, "DHCP", 0, 0, + DHCPMConstants.COOKIE_DHCP_BASE, matches, null); + logger.trace("Removing DHCP Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress); + mdsalUtil.removeFlow(flowEntity); + } else { + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, + getDhcpFlowRef(dpId, tableId, vmMacAddress),DHCPMConstants.DEFAULT_DHCP_FLOW_PRIORITY, "DHCP", 0, 0, + DHCPMConstants.COOKIE_DHCP_BASE, matches, instructions); + logger.trace("Installing DHCP Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress); + mdsalUtil.installFlow(flowEntity); + } + } + + private static String getDhcpFlowRef(BigInteger dpId, long tableId, String vmMacAddress) { + return new StringBuffer().append(DHCPMConstants.FLOWID_PREFIX) + .append(dpId).append(NwConstants.FLOWID_SEPARATOR) + .append(tableId).append(NwConstants.FLOWID_SEPARATOR) + .append(vmMacAddress).toString(); + } + + public static void setupDhcpDropAction(BigInteger dpId, short tableId, String vmMacAddress, int addOrRemove, IMdsalApiManager mdsalUtil) { + if (dpId == null || dpId.equals(DHCPMConstants.INVALID_DPID) || vmMacAddress == null) { + return; + } + List matches = getDhcpMatch(vmMacAddress); + + List actionsInfos = new ArrayList(); + List instructions = new ArrayList(); + instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfos)); + // Drop Action + actionsInfos.add(new ActionInfo(ActionType.drop_action, + new String[] {})); + if (addOrRemove == NwConstants.DEL_FLOW) { + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, + getDhcpFlowRef(dpId, tableId, vmMacAddress), + DHCPMConstants.DEFAULT_DHCP_FLOW_PRIORITY, "DHCP", 0, 0, + DHCPMConstants.COOKIE_DHCP_BASE, matches, null); + logger.trace("Removing DHCP Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress); + mdsalUtil.removeFlow(flowEntity); + } else { + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, + getDhcpFlowRef(dpId, tableId, vmMacAddress),DHCPMConstants.DEFAULT_DHCP_FLOW_PRIORITY, "DHCP", 0, 0, + DHCPMConstants.COOKIE_DHCP_BASE, matches, instructions); + logger.trace("Installing DHCP Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress); + mdsalUtil.installFlow(flowEntity); + } + } + + private static List getDhcpMatch(String vmMacAddress) { + List matches = new ArrayList(); + matches.add(new MatchInfo(MatchFieldType.eth_type, + new long[] { NwConstants.ETHTYPE_IPV4 })); + matches.add(new MatchInfo(MatchFieldType.ip_proto, + new long[] { IPProtocols.UDP.intValue() })); + matches.add(new MatchInfo(MatchFieldType.udp_src, + new long[] { DHCPMConstants.dhcpClientPort })); + matches.add(new MatchInfo(MatchFieldType.udp_dst, + new long[] { DHCPMConstants.dhcpServerPort })); + matches.add(new MatchInfo(MatchFieldType.eth_src, + new String[] { vmMacAddress })); + return matches; + } + + public static List getListOfDpns(DataBroker broker) { + List dpnsList = new LinkedList(); + InstanceIdentifier nodesInstanceIdentifier = InstanceIdentifier.builder(Nodes.class).build(); + Optional nodesOptional = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, nodesInstanceIdentifier); + if (!nodesOptional.isPresent()) { + return dpnsList; + } + Nodes nodes = nodesOptional.get(); + List nodeList = nodes.getNode(); + for (Node node : nodeList) { + NodeId nodeId = node.getId(); + if (nodeId == null) { + continue; + } + BigInteger dpnId = MDSALUtil.getDpnIdFromNodeName(nodeId); + dpnsList.add(dpnId); + } + return dpnsList; + } + + public static List getDpnsForElan(String elanInstanceName, DataBroker broker) { + List elanDpns = new LinkedList(); + InstanceIdentifier elanDpnInstanceIdentifier = InstanceIdentifier.builder(ElanDpnInterfaces.class).child(ElanDpnInterfacesList.class, new ElanDpnInterfacesListKey(elanInstanceName)).build(); + Optional elanDpnOptional = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnInstanceIdentifier); + if (elanDpnOptional.isPresent()) { + List dpns = elanDpnOptional.get().getDpnInterfaces(); + for (DpnInterfaces dpnInterfaces : dpns) { + elanDpns.add(dpnInterfaces.getDpId()); + } + } + return elanDpns; + } + + public static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface getInterfaceFromOperationalDS(String interfaceName, DataBroker dataBroker) { + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey interfaceKey = new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey(interfaceName); + InstanceIdentifier interfaceId = InstanceIdentifier.builder(InterfacesState.class).child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.class, interfaceKey).build(); + Optional interfaceOptional = MDSALUtil.read(LogicalDatastoreType.OPERATIONAL, interfaceId, dataBroker); + if (!interfaceOptional.isPresent()) { + return null; + } + return interfaceOptional.get(); + } + + + public static String getSegmentationId(Uuid networkId, DataBroker broker) { + InstanceIdentifier inst = InstanceIdentifier.create(Neutron.class).child(Networks.class).child + (Network.class, new NetworkKey(networkId)); + Optional optionalNetwork = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst); + if (!optionalNetwork.isPresent()) { + return null; + } + Network network = optionalNetwork.get(); + String segmentationId = NeutronUtils.getSegmentationIdFromNeutronNetwork(network); + return segmentationId; + } + + public static String getNodeIdFromDpnId(BigInteger dpnId) { + return MDSALUtil.NODE_PREFIX + MDSALUtil.SEPARATOR + dpnId.toString(); + } + + public static String getTrunkPortMacAddress(String parentRefName, + DataBroker broker) { + InstanceIdentifier portInstanceIdentifier = InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class); + Optional trunkPort = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, portInstanceIdentifier); + if (!trunkPort.isPresent()) { + logger.warn("Trunk port {} not available for sub-port", parentRefName); + return null; + } + return trunkPort.get().getMacAddress(); + } +} \ No newline at end of file diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpUCastMacListener.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpUCastMacListener.java new file mode 100644 index 00000000..472414ec --- /dev/null +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/DhcpUCastMacListener.java @@ -0,0 +1,112 @@ +/* + * 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.dhcpservice; + +import java.math.BigInteger; + +import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener; +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.data.LogicalDatastoreType; +import org.opendaylight.elanmanager.utils.ElanL2GwCacheUtils; +import org.opendaylight.vpnservice.datastoreutils.AsyncClusteredDataChangeListenerBase; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.neutronvpn.api.l2gw.L2GatewayDevice; +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.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation; +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.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +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; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; + +public class DhcpUCastMacListener extends AsyncClusteredDataChangeListenerBase implements AutoCloseable { + + private static final Logger logger = LoggerFactory.getLogger(DhcpUCastMacListener.class); + private DataBroker broker; + private ListenerRegistration listenerRegistration; + private DhcpExternalTunnelManager dhcpExternalTunnelManager; + + public DhcpUCastMacListener(DhcpExternalTunnelManager dhcpManager, DataBroker dataBroker) { + super(LocalUcastMacs.class, DhcpUCastMacListener.class); + this.broker = dataBroker; + this.dhcpExternalTunnelManager = dhcpManager; + } + + @Override + protected InstanceIdentifier getWildCardPath() { + return InstanceIdentifier.create(NetworkTopology.class).child(Topology.class).child(Node.class) + .augmentation(HwvtepGlobalAugmentation.class).child(LocalUcastMacs.class); + } + + @Override + public void close() throws Exception { + if (listenerRegistration != null) { + try { + listenerRegistration.close(); + } catch (final Exception e) { + logger.error("Error when cleaning up DataChangeListener.", e); + } + listenerRegistration = null; + } + logger.info("DhcpUCastMacListener Closed"); + } + + @Override + protected void remove(InstanceIdentifier identifier, + LocalUcastMacs del) { + // Flow removal for table 18 is handled in Neutron Port delete. + } + + @Override + protected void update(InstanceIdentifier identifier, + LocalUcastMacs original, LocalUcastMacs update) { + // TODO Auto-generated method stub + + } + + @Override + protected void add(InstanceIdentifier identifier, + LocalUcastMacs add) { + NodeId torNodeId = identifier.firstKeyOf(Node.class).getNodeId(); + InstanceIdentifier logicalSwitchRef = (InstanceIdentifier) add.getLogicalSwitchRef().getValue(); + Optional logicalSwitchOptional = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, logicalSwitchRef); + if ( !logicalSwitchOptional.isPresent() ) { + logger.error("Logical Switch ref doesn't have data {}", logicalSwitchRef); + return; + } + LogicalSwitches logicalSwitch = logicalSwitchOptional.get(); + String elanInstanceName = logicalSwitch.getHwvtepNodeName().getValue(); + L2GatewayDevice device = ElanL2GwCacheUtils.getL2GatewayDeviceFromCache(elanInstanceName, torNodeId.getValue()); + if (device == null) { + logger.error("Logical Switch Device with name {} is not present in L2GWCONN cache", elanInstanceName); + return; + } + IpAddress tunnelIp = device.getTunnelIp(); + BigInteger designatedDpnId = dhcpExternalTunnelManager.readDesignatedSwitchesForExternalTunnel(tunnelIp, elanInstanceName); + dhcpExternalTunnelManager.installDhcpFlowsForVms(tunnelIp, elanInstanceName, DhcpServiceUtils.getListOfDpns(broker), designatedDpnId, add.getMacEntryKey().getValue()); + } + + @Override + protected ClusteredDataChangeListener getDataChangeListener() { + return DhcpUCastMacListener.this; + } + + @Override + protected DataChangeScope getDataChangeScope() { + return DataChangeScope.SUBTREE; + } +} \ No newline at end of file diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/NodeListener.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/NodeListener.java index b7d0f2be..11bbb71a 100644 --- a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/NodeListener.java +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/vpnservice/dhcpservice/NodeListener.java @@ -7,14 +7,16 @@ */ package org.opendaylight.vpnservice.dhcpservice; +import java.math.BigInteger; +import java.util.List; + 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.dhcpservice.api.DHCPMConstants; -import org.opendaylight.vpnservice.mdsalutil.*; -import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager; -import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols; +import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener; +import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; 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; @@ -23,30 +25,27 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; - public class NodeListener extends AbstractDataChangeListener implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(NodeListener.class); - private IMdsalApiManager mdsalManager; private ListenerRegistration listenerRegistration; private final DataBroker broker; private DhcpManager dhcpManager; + private DhcpExternalTunnelManager dhcpExternalTunnelManager; - public NodeListener(final DataBroker db, final DhcpManager dhcpMgr) { + public NodeListener(final DataBroker db, final DhcpManager dhcpMgr, final DhcpExternalTunnelManager dhcpExternalTunnelManager) { super(Node.class); broker = db; dhcpManager = dhcpMgr; + this.dhcpExternalTunnelManager = dhcpExternalTunnelManager; registerListener(db); } private void registerListener(final DataBroker db) { try { listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, - getWildCardPath(), NodeListener.this, AsyncDataBroker.DataChangeScope.SUBTREE); + getWildCardPath(), NodeListener.this, AsyncDataBroker.DataChangeScope.SUBTREE); } catch (final Exception e) { LOG.error("NodeListener: DataChange listener registration fail!", e); throw new IllegalStateException("NodeListener: registration Listener failed.", e); @@ -57,10 +56,12 @@ public class NodeListener extends AbstractDataChangeListener implements Au return InstanceIdentifier.create(Nodes.class).child(Node.class); } - @Override protected void remove(InstanceIdentifier identifier, Node del) { - + NodeId nodeId = del.getId(); + BigInteger dpnId = MDSALUtil.getDpnIdFromNodeName(nodeId); + List listOfDpns = DhcpServiceUtils.getListOfDpns(broker); + dhcpExternalTunnelManager.handleDesignatedDpnDown(dpnId, listOfDpns); } @Override @@ -72,8 +73,15 @@ public class NodeListener extends AbstractDataChangeListener implements Au protected void add(InstanceIdentifier identifier, Node add) { NodeId nodeId = add.getId(); String[] node = nodeId.getValue().split(":"); + if(node.length < 2) { + LOG.warn("Unexpected nodeId {}", nodeId.getValue()); + return; + } BigInteger dpId = new BigInteger(node[1]); dhcpManager.setupTableMissForDhcpTable(dpId); + dhcpExternalTunnelManager.installDhcpDropActionOnDpn(dpId); + List listOfDpns = DhcpServiceUtils.getListOfDpns(broker); + dhcpExternalTunnelManager.handleDesignatedDpnDown(DHCPMConstants.INVALID_DPID, listOfDpns); } @Override diff --git a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/dhcpservice/impl/rev150710/DhcpServiceImplModule.java b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/dhcpservice/impl/rev150710/DhcpServiceImplModule.java index 2fa746dc..01c99cf9 100644 --- a/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/dhcpservice/impl/rev150710/DhcpServiceImplModule.java +++ b/dhcpservice/dhcpservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/dhcpservice/impl/rev150710/DhcpServiceImplModule.java @@ -3,6 +3,7 @@ package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpser import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; import org.opendaylight.vpnservice.dhcpservice.DhcpProvider; 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; public class DhcpServiceImplModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.impl.rev150710.AbstractDhcpServiceImplModule { public DhcpServiceImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { @@ -26,6 +27,8 @@ public class DhcpServiceImplModule extends org.opendaylight.yang.gen.v1.urn.open dhcpProvider.setMdsalManager(getMdsalutilDependency()); dhcpProvider.setNeutronVpnManager(getNeutronvpnDependency()); dhcpProvider.setInterfaceManagerRpc(rpcregistryDependency.getRpcService(OdlInterfaceRpcService.class)); + dhcpProvider.setItmRpcService(rpcregistryDependency.getRpcService(ItmRpcService.class)); + dhcpProvider.setEntityOwnershipService(getEntityOwnershipServiceDependency()); getBrokerDependency().registerProvider(dhcpProvider); return dhcpProvider; } diff --git a/dhcpservice/dhcpservice-impl/src/main/yang/dhcpservice-impl.yang b/dhcpservice/dhcpservice-impl/src/main/yang/dhcpservice-impl.yang index 856b7716..9a05103f 100644 --- a/dhcpservice/dhcpservice-impl/src/main/yang/dhcpservice-impl.yang +++ b/dhcpservice/dhcpservice-impl/src/main/yang/dhcpservice-impl.yang @@ -7,6 +7,7 @@ module dhcpservice-impl { import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;} import odl-mdsalutil { prefix odl-mdsal; revision-date 2015-04-10;} import neutronvpn-api { prefix nvpn; revision-date 2015-08-12;} + import opendaylight-entity-ownership-service { prefix eos; revision-date 2015-08-10;} description "Service definition for dhcpservice project"; @@ -66,6 +67,15 @@ module dhcpservice-impl { } } } + + container entity-ownership-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity eos:entity-ownership-service; + } + } + } } } } 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 index d941dec9..dd16a42f 100644 --- 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 @@ -38,4 +38,9 @@ public class L2GatewayCacheUtils { .getCache(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME); return cachedMap.get(devicename); } + + public static ConcurrentMap getCache() { + return (ConcurrentMap) CacheUtil + .getCache(L2GatewayCacheUtils.L2GATEWAY_CACHE_NAME); + } } diff --git a/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/utils/NeutronUtils.java b/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/utils/NeutronUtils.java new file mode 100644 index 00000000..5b706211 --- /dev/null +++ b/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/vpnservice/neutronvpn/api/utils/NeutronUtils.java @@ -0,0 +1,58 @@ +/* + * 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.utils; + +import java.util.List; + +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension; +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.Network; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port; +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; + +public class NeutronUtils { + public static final String VNIC_TYPE_NORMAL = "normal"; + + public 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); + } + + public static String getSegmentationIdFromNeutronNetwork(Network network) { + String segmentationId = null; + 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; + } + + static boolean isNetworkSegmentTypeVxlan(Segments providerSegment) { + Class networkType = providerSegment.getNetworkType(); + return (networkType != null && networkType.isAssignableFrom(NetworkTypeVxlan.class)); + } +} 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 d432f45c..7488a361 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 @@ -15,13 +15,14 @@ import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataCh import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener; import org.opendaylight.vpnservice.mdsalutil.MDSALUtil; +import org.opendaylight.vpnservice.neutronvpn.api.utils.NeutronUtils; +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.rev150712.Neutron; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.ElanInstances; 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.instances.ElanInstanceBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.instances.ElanInstanceKey; -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.rev150712.Neutron; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; @@ -98,7 +99,7 @@ public class NeutronNetworkChangeListener extends AbstractDataChangeListener } private void handleNeutronPortCreated(Port port) { - if (!NeutronvpnUtils.isPortVnicTypeNormal(port)) { + if (!NeutronUtils.isPortVnicTypeNormal(port)) { LOG.info("Port {} is not a NORMAL VNIC Type port; OF Port interfaces are not created", port.getUuid().getValue()); return; 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 3acc5ce4..d161f97d 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 @@ -188,26 +188,6 @@ public class NeutronvpnUtils { return null; } - protected static String getSegmentationIdFromNeutronNetwork(Network network) { - String segmentationId = null; - 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; - } - protected static List getNeutronRouterSubnetIds(DataBroker broker, Uuid routerId) { logger.info("getNeutronRouterSubnetIds for {}", routerId.getValue()); @@ -243,16 +223,6 @@ 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(); @@ -365,9 +335,4 @@ public class NeutronvpnUtils { return result; } - - static boolean isNetworkSegmentTypeVxlan(Segments providerSegment) { - Class networkType = providerSegment.getNetworkType(); - return (networkType != null && networkType.isAssignableFrom(NetworkTypeVxlan.class)); - } }