From 7b92ef5ab86ec5be8b126dd6db0a082999e9c6aa Mon Sep 17 00:00:00 2001 From: Andrej Leitner Date: Wed, 10 Aug 2016 10:43:26 +0200 Subject: [PATCH] Bug 6380 lldp-speaker - DTCL instead of DTL - lldp-speaker app converted to use DataTreeChangeListener instead of deprecated DataChangeListener - updated tests Change-Id: I8d424e8bc6c93e2addc25a9149177ca6f7b90a27 Signed-off-by: Andrej Leitner --- applications/lldp-speaker/pom.xml | 4 + .../applications/lldpspeaker/LLDPSpeaker.java | 77 +++---- .../applications/lldpspeaker/LLDPUtil.java | 47 +++-- .../NodeConnectorEventsObserver.java | 4 +- ...NodeConnectorInventoryEventTranslator.java | 189 ++++++++++-------- .../OperationalStatusChangeService.java | 3 - .../lldpspeaker/LLDPSpeakerTest.java | 22 +- ...ConnectorInventoryEventTranslatorTest.java | 172 ++++++---------- .../applications/lldpspeaker/TestUtils.java | 2 +- 9 files changed, 235 insertions(+), 285 deletions(-) diff --git a/applications/lldp-speaker/pom.xml b/applications/lldp-speaker/pom.xml index 8977cf3c47..d1c10e9de5 100644 --- a/applications/lldp-speaker/pom.xml +++ b/applications/lldp-speaker/pom.xml @@ -36,6 +36,10 @@ org.opendaylight.openflowplugin.model model-flow-service + + org.opendaylight.openflowplugin + openflowplugin-common + org.opendaylight.controller config-api diff --git a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeaker.java b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeaker.java index a785407216..5c0f71fe92 100644 --- a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeaker.java +++ b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeaker.java @@ -34,24 +34,20 @@ import org.slf4j.LoggerFactory; * Objects of this class send LLDP frames over all flow-capable ports that can * be discovered through inventory. */ -public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver, - Runnable { - - private static final Logger LOG = LoggerFactory - .getLogger(LLDPSpeaker.class); +public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver, Runnable { + private static final Logger LOG = LoggerFactory.getLogger(LLDPSpeaker.class); private static final long LLDP_FLOOD_PERIOD = 5; private final PacketProcessingService packetProcessingService; private final ScheduledExecutorService scheduledExecutorService; - private final Map, TransmitPacketInput> nodeConnectorMap = new ConcurrentHashMap<>(); + private final Map, TransmitPacketInput> nodeConnectorMap = + new ConcurrentHashMap<>(); private final ScheduledFuture scheduledSpeakerTask; private final MacAddress addressDestionation; private volatile OperStatus operationalStatus = OperStatus.RUN; - public LLDPSpeaker(final PacketProcessingService packetProcessingService, - final MacAddress addressDestionation) { - this(packetProcessingService, Executors - .newSingleThreadScheduledExecutor(), addressDestionation); + public LLDPSpeaker(final PacketProcessingService packetProcessingService, final MacAddress addressDestionation) { + this(packetProcessingService, Executors.newSingleThreadScheduledExecutor(), addressDestionation); } public void setOperationalStatus(final OperStatus operationalStatus) { @@ -67,17 +63,14 @@ public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver, } public LLDPSpeaker(final PacketProcessingService packetProcessingService, - final ScheduledExecutorService scheduledExecutorService, - final MacAddress addressDestionation) { + final ScheduledExecutorService scheduledExecutorService, + final MacAddress addressDestionation) { this.addressDestionation = addressDestionation; this.scheduledExecutorService = scheduledExecutorService; scheduledSpeakerTask = this.scheduledExecutorService - .scheduleAtFixedRate(this, LLDP_FLOOD_PERIOD, - LLDP_FLOOD_PERIOD, TimeUnit.SECONDS); + .scheduleAtFixedRate(this, LLDP_FLOOD_PERIOD,LLDP_FLOOD_PERIOD, TimeUnit.SECONDS); this.packetProcessingService = packetProcessingService; - LOG.info( - "LLDPSpeaker started, it will send LLDP frames each {} seconds", - LLDP_FLOOD_PERIOD); + LOG.info("LLDPSpeaker started, it will send LLDP frames each {} seconds", LLDP_FLOOD_PERIOD); } /** @@ -97,17 +90,11 @@ public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver, @Override public void run() { if (OperStatus.RUN.equals(operationalStatus)) { - LOG.debug("Sending LLDP frames to {} ports...", nodeConnectorMap - .keySet().size()); - - for (InstanceIdentifier nodeConnectorInstanceId : nodeConnectorMap - .keySet()) { - NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf( - nodeConnectorInstanceId).getId(); - LOG.trace("Sending LLDP through port {}", - nodeConnectorId.getValue()); - packetProcessingService.transmitPacket(nodeConnectorMap - .get(nodeConnectorInstanceId)); + LOG.debug("Sending LLDP frames to {} ports...", nodeConnectorMap.keySet().size()); + for (InstanceIdentifier nodeConnectorInstanceId : nodeConnectorMap.keySet()) { + NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId(); + LOG.trace("Sending LLDP through port {}", nodeConnectorId.getValue()); + packetProcessingService.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId)); } } } @@ -116,11 +103,9 @@ public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver, * {@inheritDoc} */ @Override - public void nodeConnectorAdded( - final InstanceIdentifier nodeConnectorInstanceId, - final FlowCapableNodeConnector flowConnector) { - NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf( - nodeConnectorInstanceId).getId(); + public void nodeConnectorAdded(final InstanceIdentifier nodeConnectorInstanceId, + final FlowCapableNodeConnector flowConnector) { + NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId(); // nodeConnectorAdded can be called even if we already sending LLDP // frames to @@ -133,16 +118,14 @@ public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver, } // Prepare to build LLDP payload - InstanceIdentifier nodeInstanceId = nodeConnectorInstanceId - .firstIdentifierOf(Node.class); + InstanceIdentifier nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class); NodeId nodeId = InstanceIdentifier.keyOf(nodeInstanceId).getId(); MacAddress srcMacAddress = flowConnector.getHardwareAddress(); Long outputPortNo = flowConnector.getPortNumber().getUint32(); // No need to send LLDP frames on local ports if (outputPortNo == null) { - LOG.trace("Port {} is local, not sending LLDP frames through it", - nodeConnectorId.getValue()); + LOG.trace("Port {} is local, not sending LLDP frames through it", nodeConnectorId.getValue()); return; } @@ -150,16 +133,14 @@ public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver, TransmitPacketInput packet = new TransmitPacketInputBuilder() .setEgress(new NodeConnectorRef(nodeConnectorInstanceId)) .setNode(new NodeRef(nodeInstanceId)) - .setPayload( - LLDPUtil.buildLldpFrame(nodeId, nodeConnectorId, - srcMacAddress, outputPortNo, - addressDestionation)).build(); + .setPayload(LLDPUtil + .buildLldpFrame(nodeId, nodeConnectorId, srcMacAddress, outputPortNo, addressDestionation)) + .build(); // Save packet to node connector id -> packet map to transmit it every 5 // seconds nodeConnectorMap.put(nodeConnectorInstanceId, packet); - LOG.trace("Port {} added to LLDPSpeaker.nodeConnectorMap", - nodeConnectorId.getValue()); + LOG.trace("Port {} added to LLDPSpeaker.nodeConnectorMap", nodeConnectorId.getValue()); // Transmit packet for first time immediately packetProcessingService.transmitPacket(packet); @@ -169,12 +150,10 @@ public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver, * {@inheritDoc} */ @Override - public void nodeConnectorRemoved( - final InstanceIdentifier nodeConnectorInstanceId) { + public void nodeConnectorRemoved(final InstanceIdentifier nodeConnectorInstanceId) { nodeConnectorMap.remove(nodeConnectorInstanceId); - NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf( - nodeConnectorInstanceId).getId(); - LOG.trace("Port {} removed from LLDPSpeaker.nodeConnectorMap", - nodeConnectorId.getValue()); + NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId(); + LOG.trace("Port {} removed from LLDPSpeaker.nodeConnectorMap", nodeConnectorId.getValue()); } + } diff --git a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPUtil.java b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPUtil.java index af7703ea63..163d1c922a 100644 --- a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPUtil.java +++ b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPUtil.java @@ -10,6 +10,7 @@ package org.opendaylight.openflowplugin.applications.lldpspeaker; import static org.opendaylight.controller.liblldp.LLDPTLV.CUSTOM_TLV_SUB_TYPE_CUSTOM_SEC; import static org.opendaylight.openflowplugin.applications.topology.lldp.utils.LLDPDiscoveryUtils.getValueForLLDPPacketIntegrityEnsuring; + import java.math.BigInteger; import java.security.NoSuchAlgorithmException; import org.apache.commons.lang3.StringUtils; @@ -33,19 +34,21 @@ public final class LLDPUtil { private static final String OF_URI_PREFIX = "openflow:"; static byte[] buildLldpFrame(final NodeId nodeId, - final NodeConnectorId nodeConnectorId, final MacAddress src, final Long outPortNo, - final MacAddress destinationAddress) { + final NodeConnectorId nodeConnectorId, + final MacAddress src, + final Long outPortNo, + final MacAddress destinationAddress) { // Create discovery pkt LLDP discoveryPkt = new LLDP(); // Create LLDP ChassisID TLV BigInteger dataPathId = dataPathIdFromNodeId(nodeId); - byte[] cidValue = LLDPTLV - .createChassisIDTLVValue(colonize(bigIntegerToPaddedHex(dataPathId))); + byte[] cidValue = LLDPTLV.createChassisIDTLVValue(colonize(bigIntegerToPaddedHex(dataPathId))); LLDPTLV chassisIdTlv = new LLDPTLV(); chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue()); chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue()) - .setLength((short) cidValue.length).setValue(cidValue); + .setLength((short) cidValue.length) + .setValue(cidValue); discoveryPkt.setChassisId(chassisIdTlv); // Create LLDP PortID TL @@ -53,15 +56,15 @@ public final class LLDPUtil { byte[] pidValue = LLDPTLV.createPortIDTLVValue(hexString); LLDPTLV portIdTlv = new LLDPTLV(); portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue()) - .setLength((short) pidValue.length).setValue(pidValue); + .setLength((short) pidValue.length) + .setValue(pidValue); portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue()); discoveryPkt.setPortId(portIdTlv); // Create LLDP TTL TLV byte[] ttl = new byte[] { (byte) 0x13, (byte) 0x37 }; LLDPTLV ttlTlv = new LLDPTLV(); - ttlTlv.setType(LLDPTLV.TLVType.TTL.getValue()) - .setLength((short) ttl.length).setValue(ttl); + ttlTlv.setType(LLDPTLV.TLVType.TTL.getValue()).setLength((short) ttl.length).setValue(ttl); discoveryPkt.setTtl(ttlTlv); // Create LLDP SystemName TLV @@ -69,15 +72,16 @@ public final class LLDPUtil { LLDPTLV systemNameTlv = new LLDPTLV(); systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue()); systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue()) - .setLength((short) snValue.length).setValue(snValue); + .setLength((short) snValue.length) + .setValue(snValue); discoveryPkt.setSystemNameId(systemNameTlv); // Create LLDP Custom TLV - byte[] customValue = LLDPTLV.createCustomTLVValue(nodeConnectorId - .getValue()); + byte[] customValue = LLDPTLV.createCustomTLVValue(nodeConnectorId.getValue()); LLDPTLV customTlv = new LLDPTLV(); customTlv.setType(LLDPTLV.TLVType.Custom.getValue()) - .setLength((short) customValue.length).setValue(customValue); + .setLength((short) customValue.length) + .setValue(customValue); discoveryPkt.addCustomTLV(customTlv); //Create LLDP CustomSec TLV @@ -87,8 +91,8 @@ public final class LLDPUtil { byte[] customSecValue = LLDPTLV.createCustomTLVValue(CUSTOM_TLV_SUB_TYPE_CUSTOM_SEC, pureValue); LLDPTLV customSecTlv = new LLDPTLV(); customSecTlv.setType(LLDPTLV.TLVType.Custom.getValue()) - .setLength((short)customSecValue.length) - .setValue(customSecValue); + .setLength((short)customSecValue.length) + .setValue(customSecValue); discoveryPkt.addCustomTLV(customSecTlv); } catch (NoSuchAlgorithmException e1) { LOG.info("LLDP extra authenticator creation failed: {}", e1.getMessage()); @@ -100,13 +104,12 @@ public final class LLDPUtil { byte[] sourceMac = HexEncode.bytesFromHexString(src.getValue()); Ethernet ethPkt = new Ethernet(); ethPkt.setSourceMACAddress(sourceMac) - .setEtherType(EtherTypes.LLDP.shortValue()) - .setPayload(discoveryPkt); + .setEtherType(EtherTypes.LLDP.shortValue()) + .setPayload(discoveryPkt); if (destinationAddress == null) { ethPkt.setDestinationMACAddress(LLDP.LLDPMulticastMac); } else { - ethPkt.setDestinationMACAddress(HexEncode - .bytesFromHexString(destinationAddress.getValue())); + ethPkt.setDestinationMACAddress(HexEncode.bytesFromHexString(destinationAddress.getValue())); } try { @@ -132,9 +135,9 @@ public final class LLDPUtil { } static byte[] buildLldpFrame(final NodeId nodeId, - final NodeConnectorId nodeConnectorId, final MacAddress srcMacAddress, - final Long outputPortNo) { - return buildLldpFrame(nodeId, nodeConnectorId, srcMacAddress, - outputPortNo, null); + final NodeConnectorId nodeConnectorId, + final MacAddress srcMacAddress, + final Long outputPortNo) { + return buildLldpFrame(nodeId, nodeConnectorId, srcMacAddress, outputPortNo, null); } } diff --git a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorEventsObserver.java b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorEventsObserver.java index 490cf6ad28..665f36dcd2 100644 --- a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorEventsObserver.java +++ b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorEventsObserver.java @@ -25,7 +25,7 @@ public interface NodeConnectorEventsObserver { * @param nodeConnectorInstanceId Object that uniquely identify added node connector * @param flowConnector object containing almost all of details about node connector */ - public void nodeConnectorAdded(InstanceIdentifier nodeConnectorInstanceId, + void nodeConnectorAdded(InstanceIdentifier nodeConnectorInstanceId, FlowCapableNodeConnector flowConnector); /** @@ -34,5 +34,5 @@ public interface NodeConnectorEventsObserver { * the same removal event. * @param nodeConnectorInstanceId Object that uniquely identify added node connector */ - public void nodeConnectorRemoved(InstanceIdentifier nodeConnectorInstanceId); + void nodeConnectorRemoved(InstanceIdentifier nodeConnectorInstanceId); } diff --git a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslator.java b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslator.java index 71b3b3e6ce..835dc97e51 100644 --- a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslator.java +++ b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslator.java @@ -8,22 +8,25 @@ package org.opendaylight.openflowplugin.applications.lldpspeaker; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; - -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.State; +import java.util.Collection; import java.util.HashMap; -import com.google.common.collect.ImmutableSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; +import javax.annotation.Nonnull; 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.AsyncDataChangeEvent; +import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.openflowplugin.common.wait.SimpleTaskRetryLooper; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortConfig; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortState; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.State; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; @@ -33,16 +36,14 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - /** * NodeConnectorInventoryEventTranslator is listening for changes in inventory operational DOM tree * and update LLDPSpeaker and topology. */ -public class NodeConnectorInventoryEventTranslator implements DataChangeListener, AutoCloseable { - /** - * - */ - private static final InstanceIdentifier II_TO_STATE +public class NodeConnectorInventoryEventTranslator + implements DataTreeChangeListener, AutoCloseable { + + private static final InstanceIdentifier II_TO_STATE = InstanceIdentifier.builder(Nodes.class) .child(Node.class) .child(NodeConnector.class) @@ -57,111 +58,141 @@ public class NodeConnectorInventoryEventTranslator implements DataChangeListener .augmentation(FlowCapableNodeConnector.class) .build(); + private static final long STARTUP_LOOP_TICK = 500L; + private static final int STARTUP_LOOP_MAX_RETRIES = 8; private static final Logger LOG = LoggerFactory.getLogger(NodeConnectorInventoryEventTranslator.class); - private final ListenerRegistration dataChangeListenerRegistration; - private final ListenerRegistration listenerOnPortStateRegistration; + private final ListenerRegistration dataChangeListenerRegistration; + private final ListenerRegistration listenerOnPortStateRegistration; private final Set observers; private final Map,FlowCapableNodeConnector> iiToDownFlowCapableNodeConnectors = new HashMap<>(); public NodeConnectorInventoryEventTranslator(DataBroker dataBroker, NodeConnectorEventsObserver... observers) { this.observers = ImmutableSet.copyOf(observers); - dataChangeListenerRegistration = dataBroker.registerDataChangeListener( - LogicalDatastoreType.OPERATIONAL, - II_TO_FLOW_CAPABLE_NODE_CONNECTOR, - this, AsyncDataBroker.DataChangeScope.BASE); - listenerOnPortStateRegistration = dataBroker.registerDataChangeListener( - LogicalDatastoreType.OPERATIONAL, - II_TO_STATE, - this, AsyncDataBroker.DataChangeScope.SUBTREE); + final DataTreeIdentifier dtiToNodeConnector = + new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, II_TO_FLOW_CAPABLE_NODE_CONNECTOR); + final DataTreeIdentifier dtiToNodeConnectorState = + new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, II_TO_STATE); + final SimpleTaskRetryLooper looper = new SimpleTaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES); + try { + dataChangeListenerRegistration = looper.loopUntilNoException(new Callable>() { + @Override + public ListenerRegistration call() throws Exception { + return dataBroker.registerDataTreeChangeListener(dtiToNodeConnector, NodeConnectorInventoryEventTranslator.this); + } + }); + listenerOnPortStateRegistration = looper.loopUntilNoException(new Callable>() { + @Override + public ListenerRegistration call() throws Exception { + return dataBroker.registerDataTreeChangeListener(dtiToNodeConnectorState, NodeConnectorInventoryEventTranslator.this); + } + }); + } catch (Exception e) { + LOG.error("DataTreeChangeListeners registration failed: {}", e); + throw new IllegalStateException("NodeConnectorInventoryEventTranslator startup failed!", e); + } + LOG.info("NodeConnectorInventoryEventTranslator has started."); } @Override public void close() { - dataChangeListenerRegistration.close(); - listenerOnPortStateRegistration.close(); + if (dataChangeListenerRegistration != null) { + dataChangeListenerRegistration.close(); + } + if (listenerOnPortStateRegistration != null) { + listenerOnPortStateRegistration.close(); + } } @Override - public void onDataChanged(AsyncDataChangeEvent, DataObject> change) { - LOG.trace("Node connectors in inventory changed: {} created, {} updated, {} removed", - change.getCreatedData().size(), change.getUpdatedData().size(), change.getRemovedPaths().size()); - - // Iterate over created node connectors - for (Map.Entry, DataObject> entry : change.getCreatedData().entrySet()) { - InstanceIdentifier nodeConnectorInstanceId = - entry.getKey().firstIdentifierOf(NodeConnector.class); - if (compareIITail(entry.getKey(),II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) { - FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) entry.getValue(); - if (!isPortDown(flowConnector)) { - notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector); - } else { - iiToDownFlowCapableNodeConnectors.put(nodeConnectorInstanceId, flowConnector); - } + public void onDataTreeChanged(@Nonnull Collection> modifications) { + for(DataTreeModification modification : modifications) { + LOG.trace("Node connectors in inventory changed -> {}", modification.getRootNode().getModificationType()); + switch (modification.getRootNode().getModificationType()) { + case WRITE: + processAddedConnector(modification); + break; + case SUBTREE_MODIFIED: + processUpdatedConnector(modification); + break; + case DELETE: + processRemovedConnector(modification); + break; + default: + throw new IllegalArgumentException("Unhandled modification type: {}" + + modification.getRootNode().getModificationType()); } } + } - // Iterate over updated node connectors (port down state may change) - for (Map.Entry, DataObject> entry : change.getUpdatedData().entrySet()) { - InstanceIdentifier nodeConnectorInstanceId = - entry.getKey().firstIdentifierOf(NodeConnector.class); - if (compareIITail(entry.getKey(),II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) { - FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) entry.getValue(); - if (isPortDown(flowConnector)) { - notifyNodeConnectorDisappeared(nodeConnectorInstanceId); - } else { - notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector); - } - } else if (compareIITail(entry.getKey(),II_TO_STATE)) { - FlowCapableNodeConnector flowNodeConnector = iiToDownFlowCapableNodeConnectors.get(nodeConnectorInstanceId); - if (flowNodeConnector != null) { - State state = (State)entry.getValue(); - if (!state.isLinkDown()) { - FlowCapableNodeConnectorBuilder flowCapableNodeConnectorBuilder = new FlowCapableNodeConnectorBuilder(flowNodeConnector); - flowCapableNodeConnectorBuilder.setState(state); - notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowCapableNodeConnectorBuilder.build()); - iiToDownFlowCapableNodeConnectors.remove(nodeConnectorInstanceId); - } - } + private void processAddedConnector(final DataTreeModification modification) { + final InstanceIdentifier identifier = modification.getRootPath().getRootIdentifier(); + InstanceIdentifier nodeConnectorInstanceId =identifier.firstIdentifierOf(NodeConnector.class); + if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) { + FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) modification.getRootNode().getDataAfter(); + if (!isPortDown(flowConnector)) { + notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector); + } else { + iiToDownFlowCapableNodeConnectors.put(nodeConnectorInstanceId, flowConnector); } } + } - // Iterate over removed node connectors - for (InstanceIdentifier removed : change.getRemovedPaths()) { - if (compareIITail(removed,II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) { - InstanceIdentifier nodeConnectorInstanceId = removed.firstIdentifierOf(NodeConnector.class); + private void processUpdatedConnector(final DataTreeModification modification) { + final InstanceIdentifier identifier = modification.getRootPath().getRootIdentifier(); + InstanceIdentifier nodeConnectorInstanceId = identifier.firstIdentifierOf(NodeConnector.class); + if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) { + FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) modification.getRootNode().getDataAfter(); + if (isPortDown(flowConnector)) { notifyNodeConnectorDisappeared(nodeConnectorInstanceId); + } else { + notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector); } + } else if (compareIITail(identifier, II_TO_STATE)) { + FlowCapableNodeConnector flowNodeConnector = iiToDownFlowCapableNodeConnectors.get(nodeConnectorInstanceId); + if (flowNodeConnector != null) { + State state = (State) modification.getRootNode().getDataAfter(); + if (!state.isLinkDown()) { + FlowCapableNodeConnectorBuilder flowCapableNodeConnectorBuilder = + new FlowCapableNodeConnectorBuilder(flowNodeConnector); + flowCapableNodeConnectorBuilder.setState(state); + notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowCapableNodeConnectorBuilder.build()); + iiToDownFlowCapableNodeConnectors.remove(nodeConnectorInstanceId); + } + } + } + } + + private void processRemovedConnector(final DataTreeModification modification) { + final InstanceIdentifier identifier = modification.getRootPath().getRootIdentifier(); + if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) { + InstanceIdentifier nodeConnectorInstanceId = identifier.firstIdentifierOf(NodeConnector.class); + notifyNodeConnectorDisappeared(nodeConnectorInstanceId); } } - /** - * @param key - * @param iiToFlowCapableNodeConnector - * @return - */ - private boolean compareIITail(InstanceIdentifier ii1, - InstanceIdentifier ii2) { + private boolean compareIITail(final InstanceIdentifier ii1, final InstanceIdentifier ii2) { return Iterables.getLast(ii1.getPathArguments()).equals(Iterables.getLast(ii2.getPathArguments())); } - private static boolean isPortDown(FlowCapableNodeConnector flowCapableNodeConnector) { + private static boolean isPortDown(final FlowCapableNodeConnector flowCapableNodeConnector) { PortState portState = flowCapableNodeConnector.getState(); PortConfig portConfig = flowCapableNodeConnector.getConfiguration(); - return portState != null && portState.isLinkDown() || - portConfig != null && portConfig.isPORTDOWN(); + return portState != null && portState.isLinkDown() + || portConfig != null && portConfig.isPORTDOWN(); } - private void notifyNodeConnectorAppeared(InstanceIdentifier nodeConnectorInstanceId, - FlowCapableNodeConnector flowConnector) { + private void notifyNodeConnectorAppeared(final InstanceIdentifier nodeConnectorInstanceId, + final FlowCapableNodeConnector flowConnector) { for (NodeConnectorEventsObserver observer : observers) { observer.nodeConnectorAdded(nodeConnectorInstanceId, flowConnector); } } - private void notifyNodeConnectorDisappeared(InstanceIdentifier nodeConnectorInstanceId) { + private void notifyNodeConnectorDisappeared(final InstanceIdentifier nodeConnectorInstanceId) { for (NodeConnectorEventsObserver observer : observers) { observer.nodeConnectorRemoved(nodeConnectorInstanceId); } } + } diff --git a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/OperationalStatusChangeService.java b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/OperationalStatusChangeService.java index f1bfd0be43..b322dbcbe3 100644 --- a/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/OperationalStatusChangeService.java +++ b/applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/OperationalStatusChangeService.java @@ -17,9 +17,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; -/** - * Created by Martin Bobak mbobak@cisco.com on 11/20/14. - */ public class OperationalStatusChangeService implements LldpSpeakerService { private final LLDPSpeaker speakerInstance; diff --git a/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeakerTest.java b/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeakerTest.java index e02784f89a..13be394b4f 100644 --- a/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeakerTest.java +++ b/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeakerTest.java @@ -19,7 +19,6 @@ import static org.mockito.Mockito.when; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,9 +44,9 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; */ @RunWith(MockitoJUnitRunner.class) public class LLDPSpeakerTest { - static InstanceIdentifier id; - static FlowCapableNodeConnector fcnc; - static TransmitPacketInput packet; + private static final InstanceIdentifier id; + private static final FlowCapableNodeConnector fcnc; + private static final TransmitPacketInput packet; static { MacAddress mac = new MacAddress("01:23:45:67:89:AB"); @@ -62,14 +61,14 @@ public class LLDPSpeakerTest { } @Mock - PacketProcessingService packetProcessingService; + private PacketProcessingService packetProcessingService; @Mock - ScheduledExecutorService scheduledExecutorService; + private ScheduledExecutorService scheduledExecutorService; @Mock - ScheduledFuture scheduledSpeakerTask; + private ScheduledFuture scheduledSpeakerTask; - MacAddress destinationMACAddress = null; - LLDPSpeaker lldpSpeaker; + private final MacAddress destinationMACAddress = null; + private LLDPSpeaker lldpSpeaker; @Before @SuppressWarnings("unchecked") @@ -161,12 +160,9 @@ public class LLDPSpeakerTest { /** * Test that lldpSpeaker cancels periodic LLDP flood task and stops - * - * @{ScheduledExecutorService . - * @throws Exception */ @Test - public void testCleanup() throws Exception { + public void testCleanup() { lldpSpeaker.close(); verify(scheduledSpeakerTask, times(1)).cancel(true); verify(scheduledExecutorService, times(1)).shutdown(); diff --git a/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslatorTest.java b/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslatorTest.java index 3426d29fcc..391624c04a 100644 --- a/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslatorTest.java +++ b/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslatorTest.java @@ -8,24 +8,30 @@ package org.opendaylight.openflowplugin.applications.lldpspeaker; -import static org.mockito.Mockito.*; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import org.junit.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.DELETE; +import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.SUBTREE_MODIFIED; +import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.WRITE; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; 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.AsyncDataChangeEvent; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; -import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -35,28 +41,20 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; */ @RunWith(MockitoJUnitRunner.class) public class NodeConnectorInventoryEventTranslatorTest { - static InstanceIdentifier id = TestUtils.createNodeConnectorId("openflow:1", "openflow:1:1"); - static InstanceIdentifier iiToConnector = id.augmentation(FlowCapableNodeConnector.class); - static FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector().build(); + private static final InstanceIdentifier id = TestUtils.createNodeConnectorId("openflow:1", "openflow:1:1"); + private static final InstanceIdentifier iiToConnector = id.augmentation(FlowCapableNodeConnector.class); + private static final FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector().build(); - @Mock DataBroker dataBroker; - @Mock ListenerRegistration dataChangeListenerRegistration; - @Mock NodeConnectorEventsObserver eventsObserver; - @Mock NodeConnectorEventsObserver eventsObserver2; + @Mock + private NodeConnectorEventsObserver eventsObserver; + @Mock + private NodeConnectorEventsObserver eventsObserver2; - MockDataChangedEvent dataChangedEvent = new MockDataChangedEvent(); - NodeConnectorInventoryEventTranslator translator; + private NodeConnectorInventoryEventTranslator translator; @Before public void setUp() { - - when(dataBroker.registerDataChangeListener( - any(LogicalDatastoreType.class), - any(InstanceIdentifier.class), - any(DataChangeListener.class), - any(AsyncDataBroker.DataChangeScope.class))) - .thenReturn(dataChangeListenerRegistration); - translator = new NodeConnectorInventoryEventTranslator(dataBroker, eventsObserver, eventsObserver2); + translator = new NodeConnectorInventoryEventTranslator(mock(DataBroker.class), eventsObserver, eventsObserver2); } /** @@ -65,11 +63,8 @@ public class NodeConnectorInventoryEventTranslatorTest { */ @Test public void testNodeConnectorCreation() { - // Setup dataChangedEvent to mock new port creation in inventory - dataChangedEvent.created.put(iiToConnector, fcnc); - - // Invoke NodeConnectorInventoryEventTranslator and check result - translator.onDataChanged(dataChangedEvent); + DataTreeModification dataTreeModification = setupDataTreeChange(WRITE, iiToConnector, fcnc); + translator.onDataTreeChanged(Collections.singleton(dataTreeModification)); verify(eventsObserver).nodeConnectorAdded(id, fcnc); } @@ -79,12 +74,8 @@ public class NodeConnectorInventoryEventTranslatorTest { @Test public void testNodeConnectorCreationLinkDown() { FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector(true, false).build(); - - // Setup dataChangedEvent to mock new port creation in inventory - dataChangedEvent.created.put(id, fcnc); - - // Invoke NodeConnectorInventoryEventTranslator and check result - translator.onDataChanged(dataChangedEvent); + DataTreeModification dataTreeModification = setupDataTreeChange(WRITE, id, fcnc); + translator.onDataTreeChanged(Collections.singleton(dataTreeModification)); verifyZeroInteractions(eventsObserver); } @@ -94,12 +85,8 @@ public class NodeConnectorInventoryEventTranslatorTest { @Test public void testNodeConnectorCreationAdminDown() { FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector(false, true).build(); - - // Setup dataChangedEvent to mock new port creation in inventory - dataChangedEvent.created.put(id, fcnc); - - // Invoke NodeConnectorInventoryEventTranslator and check result - translator.onDataChanged(dataChangedEvent); + DataTreeModification dataTreeModification = setupDataTreeChange(WRITE, id, fcnc); + translator.onDataTreeChanged(Collections.singleton(dataTreeModification)); verifyZeroInteractions(eventsObserver); } @@ -111,12 +98,8 @@ public class NodeConnectorInventoryEventTranslatorTest { @Test public void testNodeConnectorUpdateToLinkDown() { FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector(true, false).build(); - - // Setup dataChangedEvent to mock link down - dataChangedEvent.updated.put(iiToConnector, fcnc); - - // Invoke NodeConnectorInventoryEventTranslator and check result - translator.onDataChanged(dataChangedEvent); + DataTreeModification dataTreeModification = setupDataTreeChange(SUBTREE_MODIFIED, iiToConnector, fcnc); + translator.onDataTreeChanged(Collections.singleton(dataTreeModification)); verify(eventsObserver).nodeConnectorRemoved(id); } @@ -128,12 +111,8 @@ public class NodeConnectorInventoryEventTranslatorTest { @Test public void testNodeConnectorUpdateToAdminDown() { FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector(false, true).build(); - - // Setup dataChangedEvent to mock link down and administrative port down - dataChangedEvent.updated.put(iiToConnector, fcnc); - - // Invoke NodeConnectorInventoryEventTranslator and check result - translator.onDataChanged(dataChangedEvent); + DataTreeModification dataTreeModification = setupDataTreeChange(SUBTREE_MODIFIED, iiToConnector, fcnc); + translator.onDataTreeChanged(Collections.singleton(dataTreeModification)); verify(eventsObserver).nodeConnectorRemoved(id); } @@ -144,11 +123,8 @@ public class NodeConnectorInventoryEventTranslatorTest { */ @Test public void testNodeConnectorUpdateToUp() { - // Setup dataChangedEvent to mock link up and administrative port up - dataChangedEvent.updated.put(iiToConnector, fcnc); - - // Invoke NodeConnectorInventoryEventTranslator and check result - translator.onDataChanged(dataChangedEvent); + DataTreeModification dataTreeModification = setupDataTreeChange(SUBTREE_MODIFIED, iiToConnector, fcnc); + translator.onDataTreeChanged(Collections.singleton(dataTreeModification)); verify(eventsObserver).nodeConnectorAdded(id, fcnc); } @@ -158,11 +134,9 @@ public class NodeConnectorInventoryEventTranslatorTest { */ @Test public void testNodeConnectorRemoval() { - // Setup dataChangedEvent to mock node connector removal - dataChangedEvent.removed.add(iiToConnector); - + DataTreeModification dataTreeModification = setupDataTreeChange(DELETE, iiToConnector, null); // Invoke NodeConnectorInventoryEventTranslator and check result - translator.onDataChanged(dataChangedEvent); + translator.onDataTreeChanged(Collections.singleton(dataTreeModification)); verify(eventsObserver).nodeConnectorRemoved(id); } @@ -176,66 +150,32 @@ public class NodeConnectorInventoryEventTranslatorTest { // Create prerequisites InstanceIdentifier id2 = TestUtils.createNodeConnectorId("openflow:1", "openflow:1:2"); InstanceIdentifier iiToConnector2 = id2.augmentation(FlowCapableNodeConnector.class); - - // Setup dataChangedEvent to mock port creation and removal - dataChangedEvent.created.put(iiToConnector, fcnc); - dataChangedEvent.removed.add(iiToConnector2); - + List modifications = new ArrayList(); + modifications.add(setupDataTreeChange(WRITE, iiToConnector, fcnc)); + modifications.add(setupDataTreeChange(DELETE, iiToConnector2, null)); // Invoke onDataChanged and check that both observers notified - translator.onDataChanged(dataChangedEvent); + translator.onDataTreeChanged(modifications); verify(eventsObserver).nodeConnectorAdded(id, fcnc); verify(eventsObserver).nodeConnectorRemoved(id2); verify(eventsObserver2).nodeConnectorAdded(id, fcnc); verify(eventsObserver2).nodeConnectorRemoved(id2); } - /** - * Test that @{ListenerRegistration} is closed when ${NodeConnectorInventoryEventTranslator#close} - * method is called. - * @throws Exception - */ @Test - public void testCleanup() throws Exception { - // Trigger cleanup + public void tearDown() throws Exception { translator.close(); - - // Verify that ListenerRegistration to DOM events - verify(dataChangeListenerRegistration, times(2)).close(); } - static class MockDataChangedEvent implements AsyncDataChangeEvent, DataObject> { - Map,DataObject> created = new HashMap<>(); - Map,DataObject> updated = new HashMap<>(); - Set> removed = new HashSet<>(); - - @Override - public Map, DataObject> getCreatedData() { - return created; - } - - @Override - public Map, DataObject> getUpdatedData() { - return updated; - } - - @Override - public Set> getRemovedPaths() { - return removed; - } - - @Override - public Map, DataObject> getOriginalData() { - throw new UnsupportedOperationException("Not implemented by mock"); - } - - @Override - public DataObject getOriginalSubtree() { - throw new UnsupportedOperationException("Not implemented by mock"); - } - - @Override - public DataObject getUpdatedSubtree() { - throw new UnsupportedOperationException("Not implemented by mock"); - } + private DataTreeModification setupDataTreeChange(final ModificationType type, + final InstanceIdentifier ii, + final FlowCapableNodeConnector connector) { + final DataTreeModification dataTreeModification = mock(DataTreeModification.class); + when(dataTreeModification.getRootNode()).thenReturn(mock(DataObjectModification.class)); + DataTreeIdentifier identifier = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, ii); + when(dataTreeModification.getRootNode().getModificationType()).thenReturn(type); + when(dataTreeModification.getRootPath()).thenReturn(identifier); + when(dataTreeModification.getRootNode().getDataAfter()).thenReturn(connector); + return dataTreeModification; + } } diff --git a/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/TestUtils.java b/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/TestUtils.java index 422f4e55dd..e6ab666b65 100644 --- a/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/TestUtils.java +++ b/applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/TestUtils.java @@ -45,7 +45,7 @@ public class TestUtils { return createFlowCapableNodeConnector(false, false, mac, port); } - static FlowCapableNodeConnectorBuilder createFlowCapableNodeConnector(boolean linkDown, boolean adminDown, + private static FlowCapableNodeConnectorBuilder createFlowCapableNodeConnector(boolean linkDown, boolean adminDown, MacAddress mac, long port) { return new FlowCapableNodeConnectorBuilder() .setHardwareAddress(mac) -- 2.36.6