2 * Copyright (c) 2014 Pacnet and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.openflowplugin.applications.lldpspeaker;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.ThreadFactoryBuilder;
13 import java.security.NoSuchAlgorithmException;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.Executors;
17 import java.util.concurrent.Future;
18 import java.util.concurrent.ScheduledExecutorService;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.ThreadFactory;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.atomic.AtomicInteger;
23 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
24 import org.opendaylight.openflowplugin.applications.deviceownershipservice.DeviceOwnershipService;
25 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketOutput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.lldp.speaker.rev141023.OperStatus;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.opendaylight.yangtools.yang.common.RpcResult;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * Objects of this class send LLDP frames over all flow-capable ports that can
46 * be discovered through inventory.
48 public class LLDPSpeaker implements NodeConnectorEventsObserver, Runnable, AutoCloseable {
49 private static final Logger LOG = LoggerFactory.getLogger(LLDPSpeaker.class);
51 private static final long LLDP_FLOOD_PERIOD = 5;
52 private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
53 .setNameFormat("lldp-speaker-%d").setDaemon(true).build();
54 private final PacketProcessingService packetProcessingService;
55 private final ScheduledExecutorService scheduledExecutorService;
56 private final DeviceOwnershipService deviceOwnershipService;
57 private final Map<InstanceIdentifier<NodeConnector>, TransmitPacketInput> nodeConnectorMap =
58 new ConcurrentHashMap<>();
59 private final MacAddress addressDestionation;
60 private long currentFloodPeriod = LLDP_FLOOD_PERIOD;
61 private ScheduledFuture<?> scheduledSpeakerTask;
62 private volatile OperStatus operationalStatus = OperStatus.RUN;
64 public LLDPSpeaker(final PacketProcessingService packetProcessingService, final MacAddress addressDestionation,
65 final DeviceOwnershipService deviceOwnershipService) {
66 this(packetProcessingService, Executors.newSingleThreadScheduledExecutor(THREAD_FACTORY), addressDestionation,
67 deviceOwnershipService);
70 public LLDPSpeaker(final PacketProcessingService packetProcessingService,
71 final ScheduledExecutorService scheduledExecutorService,
72 final MacAddress addressDestionation,
73 final DeviceOwnershipService deviceOwnershipStatusService) {
74 this.addressDestionation = addressDestionation;
75 this.scheduledExecutorService = scheduledExecutorService;
76 this.deviceOwnershipService = deviceOwnershipStatusService;
77 scheduledSpeakerTask = this.scheduledExecutorService
78 .scheduleAtFixedRate(this, LLDP_FLOOD_PERIOD,LLDP_FLOOD_PERIOD, TimeUnit.SECONDS);
79 this.packetProcessingService = packetProcessingService;
80 LOG.info("LLDPSpeaker started, it will send LLDP frames each {} seconds", LLDP_FLOOD_PERIOD);
83 public void setOperationalStatus(final OperStatus operationalStatus) {
84 LOG.info("LLDP speaker operational status set to {}", operationalStatus);
85 this.operationalStatus = operationalStatus;
86 if (operationalStatus.equals(OperStatus.STANDBY)) {
87 nodeConnectorMap.clear();
91 public OperStatus getOperationalStatus() {
92 return operationalStatus;
95 public void setLldpFloodInterval(long time) {
96 this.currentFloodPeriod = time;
97 scheduledSpeakerTask.cancel(false);
98 scheduledSpeakerTask = this.scheduledExecutorService
99 .scheduleAtFixedRate(this, time, time, TimeUnit.SECONDS);
100 LOG.info("LLDPSpeaker restarted, it will send LLDP frames each {} seconds", time);
103 public long getLldpFloodInterval() {
104 return currentFloodPeriod;
108 * Closes this resource, relinquishing any underlying resources.
111 public void close() {
112 nodeConnectorMap.clear();
113 if (scheduledExecutorService != null) {
114 scheduledExecutorService.shutdown();
116 if (scheduledSpeakerTask != null) {
117 scheduledSpeakerTask.cancel(true);
119 LOG.trace("LLDPSpeaker stopped sending LLDP frames.");
123 * Send LLDPDU frames to all known openflow switch ports.
127 if (OperStatus.RUN.equals(operationalStatus)) {
128 LOG.debug("Sending LLDP frames to total {} ports", getOwnedPorts());
129 nodeConnectorMap.keySet().forEach(ncIID -> {
130 NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(ncIID).getId();
131 NodeId nodeId = ncIID.firstKeyOf(Node.class).getId();
132 if (deviceOwnershipService.isEntityOwned(nodeId.getValue())) {
133 LOG.debug("Node is owned by this controller, sending LLDP packet through port {}",
134 nodeConnectorId.getValue());
135 packetProcessingService.transmitPacket(nodeConnectorMap.get(ncIID));
137 LOG.debug("Node {} is not owned by this controller, so skip sending LLDP packet on port {}",
138 nodeId.getValue(), nodeConnectorId.getValue());
145 public void nodeConnectorAdded(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
146 final FlowCapableNodeConnector flowConnector) {
147 NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
149 // nodeConnectorAdded can be called even if we already sending LLDP
151 // port, so first we check if we actually need to perform any action
152 if (nodeConnectorMap.containsKey(nodeConnectorInstanceId)) {
153 LOG.debug("Port {} already in LLDPSpeaker.nodeConnectorMap, no need for additional processing",
154 nodeConnectorId.getValue());
157 // Prepare to build LLDP payload
158 InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class);
159 NodeId nodeId = InstanceIdentifier.keyOf(nodeInstanceId).getId();
160 if (!deviceOwnershipService.isEntityOwned(nodeId.getValue())) {
161 LOG.debug("Node {} is not owned by this controller, so skip sending LLDP packet on port {}",
162 nodeId.getValue(), nodeConnectorId.getValue());
165 MacAddress srcMacAddress = flowConnector.getHardwareAddress();
166 Long outputPortNo = flowConnector.getPortNumber().getUint32();
168 // No need to send LLDP frames on local ports
169 if (outputPortNo == null) {
170 LOG.debug("Port {} is local, not sending LLDP frames through it", nodeConnectorId.getValue());
174 // Generate packet with destination switch and port
175 TransmitPacketInput packet;
177 packet = new TransmitPacketInputBuilder()
178 .setEgress(new NodeConnectorRef(nodeConnectorInstanceId))
179 .setNode(new NodeRef(nodeInstanceId)).setPayload(LLDPUtil.buildLldpFrame(nodeId,
180 nodeConnectorId, srcMacAddress, outputPortNo, addressDestionation)).build();
181 } catch (NoSuchAlgorithmException | PacketException e) {
182 LOG.error("Error building LLDP frame", e);
186 // Save packet to node connector id -> packet map to transmit it periodically on the configured interval.
187 nodeConnectorMap.put(nodeConnectorInstanceId, packet);
188 LOG.debug("Port {} added to LLDPSpeaker.nodeConnectorMap", nodeConnectorId.getValue());
190 // Transmit packet for first time immediately
191 final Future<RpcResult<TransmitPacketOutput>> resultFuture = packetProcessingService.transmitPacket(packet);
192 JdkFutures.addErrorLogging(resultFuture, LOG, "transmitPacket");
196 public void nodeConnectorRemoved(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) {
197 Preconditions.checkNotNull(nodeConnectorInstanceId);
199 nodeConnectorMap.remove(nodeConnectorInstanceId);
200 NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
201 LOG.trace("Port removed from node-connector map : {}", nodeConnectorId.getValue());
204 private int getOwnedPorts() {
205 AtomicInteger ownedPorts = new AtomicInteger();
206 nodeConnectorMap.keySet().forEach(ncIID -> {
207 NodeId nodeId = ncIID.firstKeyOf(Node.class).getId();
208 if (deviceOwnershipService.isEntityOwned(nodeId.getValue())) {
209 ownedPorts.incrementAndGet();
212 return ownedPorts.get();