--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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");
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+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();
+ }
+ };
+ }
+
+}
--- /dev/null
+/*
+* 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 {
+
+}
--- /dev/null
+<?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
--- /dev/null
+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;
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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));
+ }
+}
--- /dev/null
+/*
+ * 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");
+ }
+ }
+}
--- /dev/null
+/*
+ * 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));
+ }
+
+}
<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>
<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>
<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>
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());
}
<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>
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;
//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);
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;
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);
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();
+++ /dev/null
-/**
- * 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");
- }
-}
+++ /dev/null
-/**
- * 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());
- }
- }
- }
- }
-
-}
+++ /dev/null
-/*
- * 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());
-
- }
-
-}