BUG-2242: LLDP speaker as separate application. 40/11740/8
authorAnton Frolov <anton.frolov@pacnet.com>
Sat, 4 Oct 2014 15:02:12 +0000 (19:02 +0400)
committerAnton Frolov <anton.frolov@pacnet.com>
Fri, 31 Oct 2014 13:47:54 +0000 (16:47 +0300)
 * Remove LLDPSpeaker and all related classes and methods from openflowplugin
 * Create separate LLDPSpeaker application
 * Replace usage of Timer with ScheduledExecutorService which use monotonic
   timer for delays instead of system time.
 * Decomposed LLDPSpeaker into several classes to improve unit testabilty
 * added lldp app to base distro

Change-Id: I997c74203af7461840c1c711355120b415995c06
Signed-off-by: Anton Frolov <anton.frolov@pacnet.com>
Signed-off-by: Michal Rehak <mirehak@cisco.com>
22 files changed:
applications/lldp-speaker/pom.xml [new file with mode: 0644]
applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeaker.java [new file with mode: 0644]
applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPUtil.java [new file with mode: 0644]
applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorEventsObserver.java [new file with mode: 0644]
applications/lldp-speaker/src/main/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslator.java [new file with mode: 0644]
applications/lldp-speaker/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/lldp/speaker/rev141023/LLDPSpeakerModule.java [new file with mode: 0644]
applications/lldp-speaker/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/lldp/speaker/rev141023/LLDPSpeakerModuleFactory.java [new file with mode: 0644]
applications/lldp-speaker/src/main/resources/initial/71-lldp-speaker.xml [new file with mode: 0644]
applications/lldp-speaker/src/main/yang/lldp-speaker.yang [new file with mode: 0644]
applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/LLDPSpeakerTest.java [new file with mode: 0644]
applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/NodeConnectorInventoryEventTranslatorTest.java [new file with mode: 0644]
applications/lldp-speaker/src/test/java/org/opendaylight/openflowplugin/applications/lldpspeaker/TestUtils.java [new file with mode: 0644]
applications/pom.xml
distribution/base/pom.xml
features/src/main/resources/features.xml
openflowplugin-it/src/test/java/org/opendaylight/openflowplugin/openflow/md/it/OFPaxOptionsAssistant.java
openflowplugin/pom.xml
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/MDController.java
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/sal/SalRegistrationManager.java
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeaker.java [deleted file]
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeakerPopListener.java [deleted file]
openflowplugin/src/test/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeakerPopListenerTest.java [deleted file]

diff --git a/applications/lldp-speaker/pom.xml b/applications/lldp-speaker/pom.xml
new file mode 100644 (file)
index 0000000..78f8b90
--- /dev/null
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.openflowplugin</groupId>
+        <artifactId>applications</artifactId>
+        <version>0.1.0-SNAPSHOT</version>
+    </parent>
+    <groupId>org.opendaylight.openflowplugin.applications</groupId>
+    <artifactId>lldp-speaker</artifactId>
+    <packaging>bundle</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.openflowplugin</groupId>
+            <artifactId>openflowplugin-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-broker-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller.model</groupId>
+            <artifactId>model-inventory</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller.model</groupId>
+            <artifactId>model-flow-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>liblldp</artifactId>
+        </dependency>
+
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>
+                            ${project.basedir}/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/**/*.java
+                        </exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-artifacts</id>
+                        <goals>
+                            <goal>attach-artifact</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <artifacts>
+                                <artifact>
+                                    <file>${project.build.directory}/classes/initial/71-lldp-speaker.xml</file>
+                                    <type>xml</type>
+                                    <classifier>config</classifier>
+                                </artifact>
+                            </artifacts>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
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
new file mode 100644 (file)
index 0000000..93af686
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2014 Pacnet 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.openflowplugin.applications.lldpspeaker;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+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);
+    private static final long LLDP_FLOOD_PERIOD = 5;
+
+    private final PacketProcessingService packetProcessingService;
+    private final ScheduledExecutorService scheduledExecutorService;
+    private final Map<InstanceIdentifier<NodeConnector>, TransmitPacketInput> nodeConnectorMap =
+            new ConcurrentHashMap<>();
+    private final ScheduledFuture<?> scheduledSpeakerTask;
+
+    public LLDPSpeaker(PacketProcessingService packetProcessingService) {
+        this(packetProcessingService, Executors.newSingleThreadScheduledExecutor());
+    }
+
+    public LLDPSpeaker(PacketProcessingService packetProcessingService,
+                       ScheduledExecutorService scheduledExecutorService) {
+        this.scheduledExecutorService = scheduledExecutorService;
+        scheduledSpeakerTask = this.scheduledExecutorService.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);
+    }
+
+    /**
+     * Closes this resource, relinquishing any underlying resources.
+     */
+    @Override
+    public void close() {
+        nodeConnectorMap.clear();
+        scheduledExecutorService.shutdown();
+        scheduledSpeakerTask.cancel(true);
+        LOG.trace("LLDPSpeaker stopped sending LLDP frames.");
+    }
+
+    /**
+     * Send LLDPDU frames to all known openflow switch ports.
+     */
+    @Override
+    public void run() {
+        LOG.debug("Sending LLDP frames to {} ports...", nodeConnectorMap.keySet().size());
+
+        for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap.keySet()) {
+            NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
+            LOG.trace("Sending LLDP through port {}", nodeConnectorId.getValue());
+            packetProcessingService.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void nodeConnectorAdded(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
+                                   FlowCapableNodeConnector flowConnector) {
+        NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
+
+        // nodeConnectorAdded can be called even if we already sending LLDP frames to
+        // port, so first we check if we actually need to perform any action
+        if (nodeConnectorMap.containsKey(nodeConnectorInstanceId)) {
+            LOG.trace("Port {} already in LLDPSpeaker.nodeConnectorMap, no need for additional processing",
+                    nodeConnectorId.getValue());
+            return;
+        }
+
+        // Prepare to build LLDP payload
+        InstanceIdentifier<Node> 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());
+            return;
+        }
+
+        // Generate packet with destination switch and port
+        TransmitPacketInput packet = new TransmitPacketInputBuilder()
+                .setEgress(new NodeConnectorRef(nodeConnectorInstanceId))
+                .setNode(new NodeRef(nodeInstanceId))
+                .setPayload(LLDPUtil.buildLldpFrame(nodeId, nodeConnectorId, srcMacAddress, outputPortNo))
+                .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());
+
+        // Transmit packet for first time immediately
+        packetProcessingService.transmitPacket(packet);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void nodeConnectorRemoved(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) {
+        nodeConnectorMap.remove(nodeConnectorInstanceId);
+        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
new file mode 100644 (file)
index 0000000..07a5d52
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2014 Pacnet 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.openflowplugin.applications.lldpspeaker;
+
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.controller.liblldp.Ethernet;
+import org.opendaylight.controller.liblldp.HexEncode;
+import org.opendaylight.controller.liblldp.LLDP;
+import org.opendaylight.controller.liblldp.LLDPTLV;
+import org.opendaylight.controller.liblldp.PacketException;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for dealing with LLDP packets.
+ */
+public final class LLDPUtil {
+    private static final Logger LOG = LoggerFactory.getLogger(LLDPUtil.class);
+    private static final String OF_URI_PREFIX = "openflow:";
+
+    static byte[] buildLldpFrame(NodeId nodeId, NodeConnectorId nodeConnectorId, MacAddress src, Long outPortNo) {
+        // 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);
+
+        // Create LLDP ChassisID TLV
+        BigInteger dataPathId = dataPathIdFromNodeId(nodeId);
+        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);
+
+        // Create LLDP SystemName TLV
+        byte[] snValue = LLDPTLV.createSystemNameTLVValue(nodeId.getValue());
+        LLDPTLV systemNameTlv = new LLDPTLV();
+        systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue());
+        systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue()).setLength((short) snValue.length)
+                .setValue(snValue);
+
+        // Create LLDP PortID TL
+        String hexString = Long.toHexString(outPortNo);
+        byte[] pidValue = LLDPTLV.createPortIDTLVValue(hexString);
+        LLDPTLV portIdTlv = new LLDPTLV();
+        portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue()).setLength((short) pidValue.length).setValue(pidValue);
+        portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue());
+
+        // Create LLDP Custom TLV
+        byte[] customValue = LLDPTLV.createCustomTLVValue(nodeConnectorId.getValue());
+        LLDPTLV customTlv = new LLDPTLV();
+        customTlv.setType(LLDPTLV.TLVType.Custom.getValue()).setLength((short) customValue.length)
+                .setValue(customValue);
+
+        // Create LLDP Custom Option list
+        List<LLDPTLV> customList = Collections.singletonList(customTlv);
+
+        // Create discovery pkt
+        LLDP discoveryPkt = new LLDP();
+        discoveryPkt.setChassisId(chassisIdTlv).setPortId(portIdTlv).setTtl(ttlTlv).setSystemNameId(systemNameTlv)
+                .setOptionalTLVList(customList);
+
+        // Create ethernet pkt
+        byte[] sourceMac = HexEncode.bytesFromHexString(src.getValue());
+        Ethernet ethPkt = new Ethernet();
+        ethPkt.setSourceMACAddress(sourceMac).setDestinationMACAddress(LLDP.LLDPMulticastMac)
+                .setEtherType(EtherTypes.LLDP.shortValue()).setPayload(discoveryPkt);
+
+        try {
+            return ethPkt.serialize();
+        } catch (PacketException e) {
+            LOG.error("Error creating LLDP packet",e);
+        }
+        return null;
+    }
+
+    private static String colonize(String orig) {
+        return orig.replaceAll("(?<=..)(..)", ":$1");
+    }
+
+    private static BigInteger dataPathIdFromNodeId(NodeId nodeId) {
+        String dpids = nodeId.getValue().replace(OF_URI_PREFIX, "");
+        return new BigInteger(dpids);
+    }
+
+    private static String bigIntegerToPaddedHex(BigInteger dataPathId) {
+        return StringUtils.leftPad(dataPathId.toString(16), 16, "0");
+    }
+}
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
new file mode 100644 (file)
index 0000000..490cf6a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 Pacnet 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.openflowplugin.applications.lldpspeaker;
+
+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.yang.binding.InstanceIdentifier;
+
+/**
+ * NodeConnectorEventsObserver can be added to NodeConnectorInventoryEventTranslator to receive events
+ * when node connector added or removed.
+ */
+public interface NodeConnectorEventsObserver {
+    /**
+     * This method is called when new node connector is added to inventory or when existing
+     * node connector changed it's status to UP. This method can be called multiple times for
+     * the same creation event.
+     *
+     * @param nodeConnectorInstanceId Object that uniquely identify added node connector
+     * @param flowConnector object containing almost all of details about node connector
+     */
+    public void nodeConnectorAdded(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
+                                   FlowCapableNodeConnector flowConnector);
+
+    /**
+     * This method is called when some node connector is removed from inventory or when existing
+     * node connector changed it's status to DOWN. This method can be called multiple times for
+     * the same removal event.
+     * @param nodeConnectorInstanceId Object that uniquely identify added node connector
+     */
+    public void nodeConnectorRemoved(InstanceIdentifier<NodeConnector> 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
new file mode 100644 (file)
index 0000000..2f32718
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2014 Pacnet 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.openflowplugin.applications.lldpspeaker;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Map;
+import java.util.Set;
+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.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+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.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;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+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 Logger LOG = LoggerFactory.getLogger(NodeConnectorInventoryEventTranslator.class);
+
+    private final ListenerRegistration<DataChangeListener> dataChangeListenerRegistration;
+    private final Set<NodeConnectorEventsObserver> observers;
+
+    public NodeConnectorInventoryEventTranslator(DataBroker dataBroker, NodeConnectorEventsObserver... observers) {
+        this.observers = ImmutableSet.copyOf(observers);
+        dataChangeListenerRegistration = dataBroker.registerDataChangeListener(
+                LogicalDatastoreType.OPERATIONAL,
+                InstanceIdentifier.builder(Nodes.class)
+                        .child(Node.class)
+                        .child(NodeConnector.class)
+                        .augmentation(FlowCapableNodeConnector.class)
+                        .toInstance(),
+                this, AsyncDataBroker.DataChangeScope.BASE);
+    }
+
+    @Override
+    public void close() {
+        dataChangeListenerRegistration.close();
+    }
+
+    @Override
+    public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, 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<InstanceIdentifier<?>, DataObject> entry : change.getCreatedData().entrySet()) {
+            InstanceIdentifier<NodeConnector> nodeConnectorInstanceId =
+                    entry.getKey().firstIdentifierOf(NodeConnector.class);
+
+            FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) entry.getValue();
+            if (!isPortDown(flowConnector)) {
+                notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);
+            }
+        }
+
+        // Iterate over updated node connectors (port down state may change)
+        for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : change.getUpdatedData().entrySet()) {
+            InstanceIdentifier<NodeConnector> nodeConnectorInstanceId =
+                    entry.getKey().firstIdentifierOf(NodeConnector.class);
+            FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) entry.getValue();
+            if (isPortDown(flowConnector)) {
+                notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
+            } else {
+                notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);
+            }
+        }
+
+        // Iterate over removed node connectors
+        for (InstanceIdentifier<?> removed : change.getRemovedPaths()) {
+            InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = removed.firstIdentifierOf(NodeConnector.class);
+            notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
+        }
+    }
+
+    private static boolean isPortDown(FlowCapableNodeConnector flowCapableNodeConnector) {
+        PortState portState = flowCapableNodeConnector.getState();
+        PortConfig portConfig = flowCapableNodeConnector.getConfiguration();
+        return portState != null && portState.isLinkDown() ||
+                portConfig != null && portConfig.isPORTDOWN();
+    }
+
+    private void notifyNodeConnectorAppeared(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
+                                             FlowCapableNodeConnector flowConnector) {
+        for (NodeConnectorEventsObserver observer : observers) {
+            observer.nodeConnectorAdded(nodeConnectorInstanceId, flowConnector);
+        }
+    }
+
+    private void notifyNodeConnectorDisappeared(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) {
+        for (NodeConnectorEventsObserver observer : observers) {
+            observer.nodeConnectorRemoved(nodeConnectorInstanceId);
+        }
+    }
+}
diff --git a/applications/lldp-speaker/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/lldp/speaker/rev141023/LLDPSpeakerModule.java b/applications/lldp-speaker/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/lldp/speaker/rev141023/LLDPSpeakerModule.java
new file mode 100644 (file)
index 0000000..ed135c8
--- /dev/null
@@ -0,0 +1,49 @@
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.lldp.speaker.rev141023;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.openflowplugin.applications.lldpspeaker.LLDPSpeaker;
+import org.opendaylight.openflowplugin.applications.lldpspeaker.NodeConnectorInventoryEventTranslator;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LLDPSpeakerModule extends AbstractLLDPSpeakerModule {
+    private static final Logger LOG = LoggerFactory.getLogger(LLDPSpeakerModule.class);
+
+    public LLDPSpeakerModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public LLDPSpeakerModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver,
+                             LLDPSpeakerModule oldModule, AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void customValidation() {
+        // add custom validation form module attributes here.
+    }
+
+    @Override
+    public AutoCloseable createInstance() {
+        LOG.trace("Creating LLDP speaker.");
+
+        PacketProcessingService packetProcessingService =
+                getRpcRegistryDependency().getRpcService(PacketProcessingService.class);
+
+        final LLDPSpeaker lldpSpeaker = new LLDPSpeaker(packetProcessingService);
+        final NodeConnectorInventoryEventTranslator eventTranslator =
+                new NodeConnectorInventoryEventTranslator(getDataBrokerDependency(), lldpSpeaker);
+
+        return new AutoCloseable() {
+            @Override
+            public void close() {
+                LOG.trace("Closing LLDP speaker.");
+                eventTranslator.close();
+                lldpSpeaker.close();
+            }
+        };
+    }
+
+}
diff --git a/applications/lldp-speaker/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/lldp/speaker/rev141023/LLDPSpeakerModuleFactory.java b/applications/lldp-speaker/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/lldp/speaker/rev141023/LLDPSpeakerModuleFactory.java
new file mode 100644 (file)
index 0000000..a09e921
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: lldp-speaker yang module local name: lldp-speaker
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Oct 23 10:01:14 MSK 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.lldp.speaker.rev141023;
+public class LLDPSpeakerModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.lldp.speaker.rev141023.AbstractLLDPSpeakerModuleFactory {
+
+}
diff --git a/applications/lldp-speaker/src/main/resources/initial/71-lldp-speaker.xml b/applications/lldp-speaker/src/main/resources/initial/71-lldp-speaker.xml
new file mode 100644 (file)
index 0000000..dbbe8e1
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2014 Pacnet 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
+  -->
+
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+
+<snapshot>
+  <configuration>
+    <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+      <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+        <module>
+          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:applications:lldp-speaker">prefix:lldp-speaker</type>
+          <name>lldp-speaker</name>
+          <data-broker>
+            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+            <name>binding-data-broker</name>
+          </data-broker>
+          <rpc-registry>
+            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+            <name>binding-rpc-broker</name>
+          </rpc-registry>
+        </module>
+
+      </modules>
+    </data>
+  </configuration>
+</snapshot>
\ No newline at end of file
diff --git a/applications/lldp-speaker/src/main/yang/lldp-speaker.yang b/applications/lldp-speaker/src/main/yang/lldp-speaker.yang
new file mode 100644 (file)
index 0000000..471d4a4
--- /dev/null
@@ -0,0 +1,42 @@
+module lldp-speaker {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:openflow:applications:lldp-speaker";
+    prefix "lldp-speaker";
+    import config {prefix config; revision-date 2013-04-05;}
+    import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28;}
+
+    description
+        "Application that send LLDPDU frames to all Openflow switch ports.";
+
+    revision "2014-10-23" {
+        description
+            "Initial revision";
+    }
+
+    identity lldp-speaker {
+        base "config:module-type";
+        config:java-name-prefix LLDPSpeaker;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case lldp-speaker {
+            when "/config:modules/config:module/config:type = 'lldp-speaker'";
+            container rpc-registry {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity mdsal:binding-rpc-registry;
+                    }
+                }
+            }
+            container data-broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory false;
+                        config:required-identity mdsal:binding-async-data-broker;
+                    }
+                }
+            }
+        }
+    }
+}
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
new file mode 100644 (file)
index 0000000..b872d16
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2014 Pacnet 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.openflowplugin.applications.lldpspeaker;
+
+import static org.mockito.Mockito.*;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import org.junit.*;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.CommonPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Tests for @{LLDPSpeaker} class.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class LLDPSpeakerTest {
+    static InstanceIdentifier<NodeConnector> id;
+    static FlowCapableNodeConnector fcnc;
+    static TransmitPacketInput packet;
+
+    static {
+        MacAddress mac = new MacAddress("01:23:45:67:89:AB");
+        id = TestUtils.createNodeConnectorId("openflow:1", "openflow:1:1");
+        fcnc = TestUtils.createFlowCapableNodeConnector(mac, 1L).build();
+        byte[] lldpFrame = LLDPUtil.buildLldpFrame(
+                new NodeId("openflow:1"), new NodeConnectorId("openflow:1:1"), mac, 1L);
+        packet = new TransmitPacketInputBuilder()
+                .setEgress(new NodeConnectorRef(id))
+                .setNode(new NodeRef(id.firstIdentifierOf(Node.class)))
+                .setPayload(lldpFrame).build();
+    }
+
+    @Mock PacketProcessingService packetProcessingService;
+    @Mock ScheduledExecutorService scheduledExecutorService;
+    @Mock ScheduledFuture scheduledSpeakerTask;
+    LLDPSpeaker lldpSpeaker;
+
+    @Before
+    @SuppressWarnings("unchecked")
+    public void setUp() {
+        when(scheduledExecutorService.scheduleAtFixedRate(
+                any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class)))
+                .thenReturn(scheduledSpeakerTask);
+        lldpSpeaker = new LLDPSpeaker(packetProcessingService, scheduledExecutorService);
+    }
+
+    /**
+     * Test that LLDP frame is transmitted after port appeared in inventory and
+     * periodically after that.
+     */
+    @Test
+    public void testNodeConnectorAdd() {
+        // Add node connector - LLDP packet should be transmitted through packetProcessingService
+        lldpSpeaker.nodeConnectorAdded(id, fcnc);
+
+        // Execute one iteration of periodic task - LLDP packet should be transmitted second time
+        lldpSpeaker.run();
+
+        // Check packet transmission
+        verify(packetProcessingService, times(2)).transmitPacket(packet);
+        verifyNoMoreInteractions(packetProcessingService);
+    }
+
+    /**
+     * Test that LLDP frame stop to periodically transmit after port disappeared
+     * from inventory.
+     */
+    @Test
+    public void testNodeConnectorRemoval() {
+        // Prepare for test - add node connector first
+        lldpSpeaker.nodeConnectorAdded(id, fcnc);
+
+        // Trigger removal of packet
+        lldpSpeaker.nodeConnectorRemoved(id);
+
+        // Run one iteration of LLDP flood
+        lldpSpeaker.run();
+
+        // Verify that LLDP frame sent only once (by nodeConnectorAdded),
+        // e.g. no flood after removal
+        verify(packetProcessingService, times(1)).transmitPacket(packet);
+        verifyNoMoreInteractions(packetProcessingService);
+    }
+
+    /**
+     * Test that when @{LLDPSpeaker#nodeConnectorAdded} is called multiple times
+     * with same arguments, only the first one have effect.
+     */
+    @Test
+    public void testMultipleSameNodeConnectorAddEvents() {
+        // Add node connector - LLDP packet should be transmitted through packetProcessingService
+        for (int i = 0; i < 10; i++) {
+            lldpSpeaker.nodeConnectorAdded(id, fcnc);
+        }
+
+        // Check packet transmission
+        verify(packetProcessingService, times(1)).transmitPacket(packet);
+        verifyNoMoreInteractions(packetProcessingService);
+    }
+
+    /**
+     * Test that lldpSpeaker cancels periodic LLDP flood task and stops
+     * @{ScheduledExecutorService}.
+     * @throws Exception
+     */
+    @Test
+    public void testCleanup() throws Exception {
+        lldpSpeaker.close();
+        verify(scheduledSpeakerTask, times(1)).cancel(true);
+        verify(scheduledExecutorService, times(1)).shutdown();
+    }
+
+    /**
+     * Test that checks if LLDPSpeaker working fine with local ports.
+     */
+    @Test
+    public void testLocalNodeConnectorCreation() {
+        // Call nodeConnectorAdded with local port
+        FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector()
+                .setPortNumber(new CommonPort.PortNumber("LOCAL"))
+                .build();
+        lldpSpeaker.nodeConnectorAdded(id, fcnc);
+
+        // Verify that nothing happened for local port
+        verify(packetProcessingService, never()).transmitPacket(any(TransmitPacketInput.class));
+    }
+}
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
new file mode 100644 (file)
index 0000000..a526922
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2014 Pacnet 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.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 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.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;
+
+
+/**
+ * Tests for @{NodeConnectorInventoryEventTranslator} class.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class NodeConnectorInventoryEventTranslatorTest {
+    static InstanceIdentifier<NodeConnector> id = TestUtils.createNodeConnectorId("openflow:1", "openflow:1:1");
+    static FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector().build();
+
+    @Mock DataBroker dataBroker;
+    @Mock ListenerRegistration<DataChangeListener> dataChangeListenerRegistration;
+    @Mock NodeConnectorEventsObserver eventsObserver;
+    @Mock NodeConnectorEventsObserver eventsObserver2;
+
+    MockDataChangedEvent dataChangedEvent = new MockDataChangedEvent();
+    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);
+    }
+
+    /**
+     * Test that checks if @{NodeConnectorEventsObserver#nodeConnectorAdded} is called
+     * for each FlowCapableNodeConnector item that @{AsyncDataChangeEvent#getCreatedData} return.
+     */
+    @Test
+    public void testNodeConnectorCreation() {
+        // Setup dataChangedEvent to mock new port creation in inventory
+        dataChangedEvent.created.put(id, fcnc);
+
+        // Invoke NodeConnectorInventoryEventTranslator and check result
+        translator.onDataChanged(dataChangedEvent);
+        verify(eventsObserver).nodeConnectorAdded(id, fcnc);
+    }
+
+    /**
+     * Test that checks that nothing is called when port appeared in inventory in link down state.
+     */
+    @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);
+        verifyZeroInteractions(eventsObserver);
+    }
+
+    /**
+     * Test that checks that nothing is called when port appeared in inventory in admin down state.
+     */
+    @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);
+        verifyZeroInteractions(eventsObserver);
+    }
+
+    /**
+     * Test that checks if @{NodeConnectorEventsObserver#nodeConnectorRemoved} is called
+     * for each FlowCapableNodeConnector item inside @{AsyncDataChangeEvent#getUpdatedData}
+     * that have link down state.
+     */
+    @Test
+    public void testNodeConnectorUpdateToLinkDown() {
+        FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector(true, false).build();
+
+        // Setup dataChangedEvent to mock link down
+        dataChangedEvent.updated.put(id, fcnc);
+
+        // Invoke NodeConnectorInventoryEventTranslator and check result
+        translator.onDataChanged(dataChangedEvent);
+        verify(eventsObserver).nodeConnectorRemoved(id);
+    }
+
+    /**
+     * Test that checks if @{NodeConnectorEventsObserver#nodeConnectorRemoved} is called
+     * for each FlowCapableNodeConnector item inside @{AsyncDataChangeEvent#getUpdatedData}
+     * that have administrative down state.
+     */
+    @Test
+    public void testNodeConnectorUpdateToAdminDown() {
+        FlowCapableNodeConnector fcnc = TestUtils.createFlowCapableNodeConnector(false, true).build();
+
+        // Setup dataChangedEvent to mock link down and administrative port down
+        dataChangedEvent.updated.put(id, fcnc);
+
+        // Invoke NodeConnectorInventoryEventTranslator and check result
+        translator.onDataChanged(dataChangedEvent);
+        verify(eventsObserver).nodeConnectorRemoved(id);
+    }
+
+    /**
+     * Test that checks if @{NodeConnectorEventsObserver#nodeConnectorAdded} is called
+     * for each FlowCapableNodeConnector item inside @{AsyncDataChangeEvent#getUpdatedData}
+     * that have administrative up and link up state.
+     */
+    @Test
+    public void testNodeConnectorUpdateToUp() {
+        // Setup dataChangedEvent to mock link up and administrative port up
+        dataChangedEvent.updated.put(id, fcnc);
+
+        // Invoke NodeConnectorInventoryEventTranslator and check result
+        translator.onDataChanged(dataChangedEvent);
+        verify(eventsObserver).nodeConnectorAdded(id, fcnc);
+    }
+
+    /**
+     * Test that checks if @{NodeConnectorEventsObserver#nodeConnectorRemoved} is called
+     * for each FlowCapableNodeConnector path that @{AsyncDataChangeEvent#getRemovedPaths} return.
+     */
+    @Test
+    public void testNodeConnectorRemoval() {
+        // Setup dataChangedEvent to mock node connector removal
+        dataChangedEvent.removed.add(id);
+
+        // Invoke NodeConnectorInventoryEventTranslator and check result
+        translator.onDataChanged(dataChangedEvent);
+        verify(eventsObserver).nodeConnectorRemoved(id);
+    }
+
+    /**
+     * Test that checks if @{NodeConnectorEventsObserver#nodeConnectorAdded} and
+     * @{NodeConnectorEventsObserver#nodeConnectorRemoved} are called for each
+     * observer when multiple observers are registered for notifications.
+     */
+    @Test
+    public  void testMultipleObserversNotified() throws Exception {
+        // Create prerequisites
+        InstanceIdentifier<NodeConnector> id2 = TestUtils.createNodeConnectorId("openflow:1", "openflow:1:2");
+
+        // Setup dataChangedEvent to mock port creation and removal
+        dataChangedEvent.created.put(id, fcnc);
+        dataChangedEvent.removed.add(id2);
+
+        // Invoke onDataChanged and check that both observers notified
+        translator.onDataChanged(dataChangedEvent);
+        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
+        translator.close();
+
+        // Verify that ListenerRegistration to DOM events
+        verify(dataChangeListenerRegistration).close();
+    }
+
+    static class MockDataChangedEvent implements AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> {
+        Map<InstanceIdentifier<?>,DataObject> created = new HashMap<>();
+        Map<InstanceIdentifier<?>,DataObject> updated = new HashMap<>();
+        Set<InstanceIdentifier<?>> removed = new HashSet<>();
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
+            return created;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
+            return updated;
+        }
+
+        @Override
+        public Set<InstanceIdentifier<?>> getRemovedPaths() {
+            return removed;
+        }
+
+        @Override
+        public Map<InstanceIdentifier<?>, 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");
+        }
+    }
+}
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
new file mode 100644 (file)
index 0000000..7445f3f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014 Pacnet 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.openflowplugin.applications.lldpspeaker;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.CommonPort;
+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.flow.capable.port.StateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+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.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Helper methods that are used by multiple tests.
+ */
+public class TestUtils {
+    static InstanceIdentifier<NodeConnector> createNodeConnectorId(String nodeKey, String nodeConnectorKey) {
+        return InstanceIdentifier.builder(Nodes.class)
+                .child(Node.class, new NodeKey(new NodeId(nodeKey)))
+                .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(nodeConnectorKey)))
+                .build();
+    }
+
+    static FlowCapableNodeConnectorBuilder createFlowCapableNodeConnector() {
+        return createFlowCapableNodeConnector(false, false);
+    }
+
+    static FlowCapableNodeConnectorBuilder createFlowCapableNodeConnector(boolean linkDown, boolean adminDown) {
+        return createFlowCapableNodeConnector(linkDown, adminDown, new MacAddress("13:37:BA:AD:F0:0D"), 1L);
+    }
+
+    static FlowCapableNodeConnectorBuilder createFlowCapableNodeConnector(MacAddress mac, long port) {
+        return createFlowCapableNodeConnector(false, false, mac, port);
+    }
+
+    static FlowCapableNodeConnectorBuilder createFlowCapableNodeConnector(boolean linkDown, boolean adminDown,
+                                                                          MacAddress mac, long port) {
+        return new FlowCapableNodeConnectorBuilder()
+                .setHardwareAddress(mac)
+                .setPortNumber(new CommonPort.PortNumber(port))
+                .setState(new StateBuilder().setLinkDown(linkDown).build())
+                .setConfiguration(new PortConfig(false, false, false, adminDown));
+    }
+
+}
index 709b06a2bebc81fdfdafe4b5f5c50f2c4a5c71e7..066bd03c03143f1ca5cc8bc6e783202cb4e85fd3 100644 (file)
     <modules>
         <module>table-miss-enforcer</module>
         <module>of-switch-config-pusher</module>
-  </modules>
+        <module>lldp-speaker</module>
+    </modules>
 
-</project>
\ No newline at end of file
+</project>
index 3824dda769612550b8271a05ffb89dd77721232c..e39c542a09452ac1fddfc05a841428919d687887 100644 (file)
@@ -126,6 +126,12 @@ see https://git.opendaylight.org/gerrit/#/c/390/
           <artifactId>table-miss-enforcer</artifactId>
           <version>${project.version}</version>
       </dependency>
+      <dependency>
+          <groupId>org.opendaylight.openflowplugin.applications</groupId>
+          <artifactId>lldp-speaker</artifactId>
+          <version>${project.version}</version>
+      </dependency>
+
   </dependencies>
 
   <build>
index 4a3cd5c89c3c1bf70f01c9a89c3c31f904b6ce65..3832a09ff5310fde79a4a3c7fb0fc04071458036 100644 (file)
@@ -54,5 +54,8 @@
 
         <bundle>mvn:org.opendaylight.openflowplugin.applications/of-switch-config-pusher/${project.version}</bundle>
         <configfile finalname="etc/opendaylight/karaf/70-of-switch-config-pusher.xml">mvn:org.opendaylight.openflowplugin.applications/of-switch-config-pusher/${project.version}/xml/config</configfile>
+
+      <bundle>mvn:org.opendaylight.openflowplugin.applications/lldp-speaker/${project.version}</bundle>
+      <configfile finalname="etc/opendaylight/karaf/71-lldp-speaker.xml">mvn:org.opendaylight.openflowplugin.applications/lldp-speaker/${project.version}/xml/config</configfile>
     </feature>
 </features>
index 391512a249fb18e41ddd1a27384c4c44114a31a2..ed62919663ff933163aaf104a6711e968206854f 100644 (file)
@@ -101,7 +101,6 @@ public abstract class OFPaxOptionsAssistant {
         return new DefaultCompositeOption(
 //                mavenBundle("org.apache.felix", "org.apache.felix.dependencymanager").versionAsInProject(),
 //                mavenBundle(CONTROLLER, "sal").versionAsInProject(),
-                mavenBundle(CONTROLLER, "liblldp").versionAsInProject(),
                 mavenBundle(YANGTOOLS + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject());
 
     }
index 325de46b1fea6e2278741bd6a0b549f06bc7571e..4a77e4683c26ea2d2cfcfb53d769fddf4b6ce9fe 100644 (file)
             <artifactId>openflowplugin-extension-api</artifactId>
             <version>${project.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>liblldp</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.opendaylight.controller.model</groupId>
             <artifactId>model-flow-base</artifactId>
index bd41e3d191efbc13a2f4079c8dad40ff240751c2..73bec508642cfc856910326d4d35d22340f1d97f 100644 (file)
@@ -45,7 +45,6 @@ import org.opendaylight.openflowplugin.openflow.md.core.translator.NotificationP
 import org.opendaylight.openflowplugin.openflow.md.core.translator.PacketInTranslator;
 import org.opendaylight.openflowplugin.openflow.md.core.translator.PacketInV10Translator;
 import org.opendaylight.openflowplugin.openflow.md.core.translator.PortStatusMessageToNodeConnectorUpdatedTranslator;
-import org.opendaylight.openflowplugin.openflow.md.lldp.LLDPSpeakerPopListener;
 import org.opendaylight.openflowplugin.api.openflow.md.queue.PopListener;
 import org.opendaylight.openflowplugin.openflow.md.util.OpenflowPortsUtil;
 import org.opendaylight.openflowplugin.api.statistics.MessageSpy;
@@ -213,10 +212,6 @@ public class MDController implements IMDController, AutoCloseable {
         //Notification registration for queue-statistics
         addMessagePopListener(QueueStatisticsUpdate.class, notificationPopListener);
 
-        //Notification for LLDPSpeaker
-        LLDPSpeakerPopListener<DataObject> lldpPopListener  = new LLDPSpeakerPopListener<DataObject>();
-        addMessagePopListener(NodeConnectorUpdated.class,lldpPopListener);
-
         // Push the updated Listeners to Session Manager which will be then picked up by ConnectionConductor eventually
         OFSessionUtil.getSessionManager().setTranslatorMapping(messageTranslators);
         OFSessionUtil.getSessionManager().setPopListenerMapping(popListeners);
index ee5e8e951c8014c1d64f751078ddbc4c4dccb6ae..ec62918fc5a06e69a57626c2d60a140d5cb41ea9 100644 (file)
@@ -23,7 +23,6 @@ import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionConte
 import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionListener;
 import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionManager;
 import org.opendaylight.openflowplugin.api.openflow.md.core.session.SwitchSessionKeyOF;
-import org.opendaylight.openflowplugin.openflow.md.lldp.LLDPSpeaker;
 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.inet.types.rev100924.Ipv4Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
@@ -99,7 +98,6 @@ public class SalRegistrationManager implements SessionListener, AutoCloseable {
         NodeRef nodeRef = new NodeRef(identifier);
         NodeId nodeId = nodeIdFromDatapathId(datapathId);
         ModelDrivenSwitchImpl ofSwitch = new ModelDrivenSwitchImpl(nodeId, identifier, context);
-        LLDPSpeaker.getInstance().addModelDrivenSwitch(identifier, ofSwitch);
         CompositeObjectRegistration<ModelDrivenSwitch> registration = ofSwitch.register(providerContext);
         context.setProviderRegistration(registration);
 
@@ -118,7 +116,6 @@ public class SalRegistrationManager implements SessionListener, AutoCloseable {
         InstanceIdentifier<Node> identifier = identifierFromDatapathId(datapathId);
         NodeRef nodeRef = new NodeRef(identifier);
         NodeRemoved nodeRemoved = nodeRemoved(nodeRef);
-        LLDPSpeaker.getInstance().removeModelDrivenSwitch(identifier);
         if (context.isValid()) {
             CompositeObjectRegistration<ModelDrivenSwitch> registration = context.getProviderRegistration();
             registration.close();
diff --git a/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeaker.java b/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeaker.java
deleted file mode 100644 (file)
index 2f97e13..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/**
- * Copyright (c) 2013 Cisco Systems, Inc. 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.openflowplugin.openflow.md.lldp;
-
-import java.math.BigInteger;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.ConcurrentHashMap;
-import org.opendaylight.controller.liblldp.EtherTypes;
-import org.opendaylight.controller.liblldp.Ethernet;
-import org.opendaylight.controller.liblldp.HexEncode;
-import org.opendaylight.controller.liblldp.LLDP;
-import org.opendaylight.controller.liblldp.LLDPTLV;
-import org.opendaylight.controller.liblldp.PacketException;
-import org.opendaylight.openflowplugin.api.openflow.md.util.OpenflowVersion;
-import org.opendaylight.openflowplugin.api.openflow.md.ModelDrivenSwitch;
-import org.opendaylight.openflowplugin.openflow.md.util.InventoryDataServiceUtil;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-public class LLDPSpeaker {
-    private static Logger LOG = LoggerFactory.getLogger(LLDPSpeaker.class);
-
-    private final Map<InstanceIdentifier<NodeConnector>, TransmitPacketInput> nodeConnectorMap = new ConcurrentHashMap<InstanceIdentifier<NodeConnector>, TransmitPacketInput>();
-    private final Map<InstanceIdentifier<Node>, ModelDrivenSwitch> nodeMap = new ConcurrentHashMap<InstanceIdentifier<Node>, ModelDrivenSwitch>();
-    private static final LLDPSpeaker instance = new LLDPSpeaker();
-    private Timer timer = new Timer();
-    private static final int DELAY = 0;
-    private static final int PERIOD = 1000 * 5;
-
-    private LLDPSpeaker() {
-        timer.schedule(new LLDPSpeakerTask(), DELAY, PERIOD);
-    }
-
-    public static LLDPSpeaker getInstance() {
-        return instance;
-    }
-
-    public void addModelDrivenSwitch(InstanceIdentifier<Node> nodeInstanceId, ModelDrivenSwitch sw) {
-        nodeMap.put(nodeInstanceId, sw);
-    }
-
-    public void removeModelDrivenSwitch(InstanceIdentifier<Node> nodeInstanceId) {
-        nodeMap.remove(nodeInstanceId);
-        for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap.keySet()) {
-            if (nodeInstanceId.equals(nodeConnectorInstanceId.firstIdentifierOf(Node.class))) {
-                nodeConnectorMap.remove(nodeConnectorInstanceId);
-            }
-        }
-    }
-
-    public void addNodeConnector(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId, NodeConnector nodeConnector) {
-        InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class);
-        ModelDrivenSwitch md = nodeMap.get(nodeInstanceId);
-
-        NodeKey nodeKey = InstanceIdentifier.keyOf(nodeInstanceId);
-        NodeId nodeId = nodeKey.getId();
-        NodeConnectorId nodeConnectorId = nodeConnector.getId();
-        FlowCapableNodeConnector flowConnector = nodeConnector.<FlowCapableNodeConnector>getAugmentation(FlowCapableNodeConnector.class);
-        TransmitPacketInputBuilder tpib = new TransmitPacketInputBuilder();
-        tpib.setEgress(new NodeConnectorRef(nodeConnectorInstanceId));
-        tpib.setNode(new NodeRef(nodeInstanceId));
-        if(nodeInstanceId == null) {
-            LOG.warn("addNodeConnector(): nodeInstanceId should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
-        } else if (nodeConnectorInstanceId == null) {
-            LOG.warn("addNodeConnector(): nodeConnectorInstanceId should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
-        } else if (flowConnector == null) {
-            LOG.warn("addNodeConnector(): flowConnector should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
-        } else if (md == null) {
-            LOG.debug("addNodeConnector(): md is null, this usually means your switch disconnected while you had unprocessed NodeConnectorUpdated messages in queue nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
-        } else if(md.getSessionContext() == null) {
-            LOG.warn("addNodeConnector(): md.getSessionContext() should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
-        } else if (md.getSessionContext().getPrimaryConductor() == null) {
-            LOG.warn("addNodeConnector(): md.getSessionContext().getPrimaryConductor() should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
-        } else {
-            tpib.setPayload(lldpDataFrom(nodeInstanceId,nodeConnectorInstanceId,flowConnector.getHardwareAddress(),
-                    md.getSessionContext().getPrimaryConductor().getVersion()));
-            nodeConnectorMap.put(nodeConnectorInstanceId, tpib.build());
-
-            md.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId));
-        }
-    }
-
-    public void removeNodeConnector(
-            InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
-            NodeConnector nodeConnector) {
-        nodeConnectorMap.remove(nodeConnectorInstanceId);
-    }
-
-    private byte[] lldpDataFrom(InstanceIdentifier<Node> nodeInstanceId, InstanceIdentifier<NodeConnector> nodeConnectorInstanceId, MacAddress src,
-                                Short version) {
-
-        NodeId nodeId = InstanceIdentifier.keyOf(nodeInstanceId).getId();
-        NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
-        // Create LLDP TTL TLV
-        byte[] ttl = new byte[]{(byte) 0, (byte) 120};
-        LLDPTLV ttlTlv = new LLDPTLV();
-        ttlTlv.setType(LLDPTLV.TLVType.TTL.getValue()).setLength((short) ttl.length).setValue(ttl);
-
-        // Create LLDP ChassisID TLV
-        BigInteger dataPathId = InventoryDataServiceUtil.dataPathIdFromNodeId(nodeId);
-        byte[] cidValue = LLDPTLV.createChassisIDTLVValue(
-                colonize(InventoryDataServiceUtil.bigIntegerToPaddedHex(dataPathId)));
-        LLDPTLV chassisIdTlv = new LLDPTLV();
-        chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue());
-        chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue()).setLength((short) cidValue.length)
-                .setValue(cidValue);
-
-        // Create LLDP SystemName TLV
-        byte[] snValue = LLDPTLV.createSystemNameTLVValue(nodeId.getValue());
-        LLDPTLV systemNameTlv = new LLDPTLV();
-        systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue());
-        systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue()).setLength((short) snValue.length)
-                .setValue(snValue);
-
-        // Create LLDP PortID TL
-        Long portNo = InventoryDataServiceUtil.portNumberfromNodeConnectorId(OpenflowVersion.get(version), nodeConnectorId);
-
-        String hexString = Long.toHexString(portNo);
-        byte[] pidValue = LLDPTLV.createPortIDTLVValue(hexString);
-        LLDPTLV portIdTlv = new LLDPTLV();
-        portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue()).setLength((short) pidValue.length).setValue(pidValue);
-        portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue());
-
-        // Create LLDP Custom TLV
-        byte[] customValue = LLDPTLV.createCustomTLVValue(nodeConnectorId.getValue());
-        LLDPTLV customTlv = new LLDPTLV();
-        customTlv.setType(LLDPTLV.TLVType.Custom.getValue()).setLength((short) customValue.length)
-                .setValue(customValue);
-
-        // Create LLDP Custom Option list
-        List<LLDPTLV> customList = Collections.singletonList(customTlv);
-
-        // Create discovery pkt
-        LLDP discoveryPkt = new LLDP();
-        discoveryPkt.setChassisId(chassisIdTlv).setPortId(portIdTlv).setTtl(ttlTlv).setSystemNameId(systemNameTlv)
-                .setOptionalTLVList(customList);
-
-        // Create ethernet pkt
-        byte[] sourceMac = HexEncode.bytesFromHexString(src.getValue());
-        Ethernet ethPkt = new Ethernet();
-        ethPkt.setSourceMACAddress(sourceMac).setDestinationMACAddress(LLDP.LLDPMulticastMac)
-                .setEtherType(EtherTypes.LLDP.shortValue()).setPayload(discoveryPkt);
-
-        try {
-            byte[] data = ethPkt.serialize();
-            return data;
-        } catch (PacketException e) {
-            LOG.error("Error creating LLDP packet", e);
-        }
-        return null;
-    }
-
-    private class LLDPSpeakerTask extends TimerTask {
-
-        @Override
-        public void run() {
-            for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap.keySet()) {
-                InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class);
-                ModelDrivenSwitch md = nodeMap.get(nodeInstanceId);
-                md.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId));
-            }
-
-        }
-
-    }
-
-    private String colonize(String orig) {
-        return orig.replaceAll("(?<=..)(..)", ":$1");
-    }
-}
diff --git a/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeakerPopListener.java b/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeakerPopListener.java
deleted file mode 100644 (file)
index f6aacc8..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * Copyright (c) 2013 Cisco Systems, Inc. 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.openflowplugin.openflow.md.lldp;
-
-import org.opendaylight.openflowplugin.api.openflow.md.queue.PopListener;
-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.inventory.rev130819.FlowCapableNodeConnectorUpdated;
-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.inventory.rev130819.NodeConnectorUpdated;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-public class LLDPSpeakerPopListener<T> implements PopListener<T> {
-
-
-    @Override
-    public void onPop(T processedMessage) {
-        if (processedMessage instanceof NodeConnectorUpdated) {
-            NodeConnectorUpdated connector = (NodeConnectorUpdated) processedMessage;
-            FlowCapableNodeConnectorUpdated flowConnector = connector.<FlowCapableNodeConnectorUpdated>getAugmentation(FlowCapableNodeConnectorUpdated.class);
-            if (flowConnector != null) {
-                InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = (InstanceIdentifier<NodeConnector>) connector.getNodeConnectorRef().getValue();
-                NodeConnectorBuilder ncb = new NodeConnectorBuilder(connector);
-                FlowCapableNodeConnectorBuilder fcncb = new FlowCapableNodeConnectorBuilder(flowConnector);
-                ncb.addAugmentation(FlowCapableNodeConnector.class, fcncb.build());
-                PortState portState = flowConnector.getState();
-                PortConfig portConfig = flowConnector.getConfiguration();
-                if ((portState == null || !portState.isLinkDown()) && (portConfig == null || !portConfig.isPORTDOWN())) {
-                    LLDPSpeaker.getInstance().addNodeConnector(nodeConnectorInstanceId, ncb.build());
-                } else {
-                    LLDPSpeaker.getInstance().removeNodeConnector(nodeConnectorInstanceId, ncb.build());
-                }
-            }
-        }
-    }
-
-}
diff --git a/openflowplugin/src/test/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeakerPopListenerTest.java b/openflowplugin/src/test/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeakerPopListenerTest.java
deleted file mode 100644 (file)
index dff4e01..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. 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.openflowplugin.openflow.md.lldp;
-
-import static org.mockito.Mockito.when;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.opendaylight.openflowplugin.api.OFConstants;
-import org.opendaylight.openflowplugin.api.openflow.md.ModelDrivenSwitch;
-import org.opendaylight.openflowplugin.api.openflow.md.core.ConnectionConductor;
-import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionContext;
-import org.opendaylight.openflowplugin.openflow.md.util.OpenflowPortsUtil;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdatedBuilder;
-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.flow.capable.port.StateBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdatedBuilder;
-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.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Created by Martin Bobak mbobak@cisco.com on 9/23/14.
- */
-@RunWith(MockitoJUnitRunner.class)
-public class LLDPSpeakerPopListenerTest {
-
-    @MockitoAnnotations.Mock
-    private ModelDrivenSwitch modelDrivenSwitch;
-    @MockitoAnnotations.Mock
-    private SessionContext context;
-    @MockitoAnnotations.Mock
-    private ConnectionConductor connectionConductor;
-
-
-    @Before
-    public void setup() {
-        when(modelDrivenSwitch.getSessionContext()).thenReturn(context);
-        when(context.getPrimaryConductor()).thenReturn(connectionConductor);
-        when(connectionConductor.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
-        OpenflowPortsUtil.init();
-    }
-
-    @Test
-    /**
-     * Test method which verifies registration of nodeConnector in LLDPSpeaker. 
-     */
-    public void TestOnPop() {
-        LLDPSpeakerPopListener lldpSpeakerPopListener = new LLDPSpeakerPopListener();
-        NodeConnectorUpdatedBuilder nodeConnectorUpdatedBuilder = new NodeConnectorUpdatedBuilder();
-        FlowCapableNodeConnectorUpdatedBuilder flowCapableNodeConnectorUpdatedBuilder = new FlowCapableNodeConnectorUpdatedBuilder();
-
-        StateBuilder stateBuilder = new StateBuilder();
-        stateBuilder.setBlocked(false);
-        stateBuilder.setLinkDown(false);
-        stateBuilder.setLive(true);
-        flowCapableNodeConnectorUpdatedBuilder.setState(stateBuilder.build());
-
-        flowCapableNodeConnectorUpdatedBuilder.setConfiguration(new PortConfig(true, true, true, false));
-        flowCapableNodeConnectorUpdatedBuilder.setHardwareAddress(new MacAddress("00:00:00:00:00:00"));
-        nodeConnectorUpdatedBuilder.addAugmentation(FlowCapableNodeConnectorUpdated.class, flowCapableNodeConnectorUpdatedBuilder.build());
-        NodeConnectorId nodeConnectorId = new NodeConnectorId("1");
-        nodeConnectorUpdatedBuilder.setId(nodeConnectorId);
-        InstanceIdentifier instanceIdentifier = InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId("1"))).child(NodeConnector.class, new NodeConnectorKey(nodeConnectorId));
-        NodeConnectorRef nodeConnectorRef = new NodeConnectorRef(instanceIdentifier);
-
-        LLDPSpeaker.getInstance().addModelDrivenSwitch(instanceIdentifier.firstIdentifierOf(Node.class), modelDrivenSwitch);
-
-        nodeConnectorUpdatedBuilder.setNodeConnectorRef(nodeConnectorRef);
-        lldpSpeakerPopListener.onPop(nodeConnectorUpdatedBuilder.build());
-
-    }
-
-}