BUG-2242: LLDP speaker as separate application.
[openflowplugin.git] / applications / lldp-speaker / src / main / java / org / opendaylight / openflowplugin / applications / lldpspeaker / LLDPSpeaker.java
1 /*
2  * Copyright (c) 2014 Pacnet and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.openflowplugin.applications.lldpspeaker;
10
11 import java.util.Map;
12 import java.util.concurrent.ConcurrentHashMap;
13 import java.util.concurrent.Executors;
14 import java.util.concurrent.ScheduledExecutorService;
15 import java.util.concurrent.ScheduledFuture;
16 import java.util.concurrent.TimeUnit;
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.*;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
25 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30  * Objects of this class send LLDP frames over all flow-capable ports that can
31  * be discovered through inventory.
32  */
33 public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver, Runnable {
34     private static final Logger LOG = LoggerFactory.getLogger(LLDPSpeaker.class);
35     private static final long LLDP_FLOOD_PERIOD = 5;
36
37     private final PacketProcessingService packetProcessingService;
38     private final ScheduledExecutorService scheduledExecutorService;
39     private final Map<InstanceIdentifier<NodeConnector>, TransmitPacketInput> nodeConnectorMap =
40             new ConcurrentHashMap<>();
41     private final ScheduledFuture<?> scheduledSpeakerTask;
42
43     public LLDPSpeaker(PacketProcessingService packetProcessingService) {
44         this(packetProcessingService, Executors.newSingleThreadScheduledExecutor());
45     }
46
47     public LLDPSpeaker(PacketProcessingService packetProcessingService,
48                        ScheduledExecutorService scheduledExecutorService) {
49         this.scheduledExecutorService = scheduledExecutorService;
50         scheduledSpeakerTask = this.scheduledExecutorService.scheduleAtFixedRate(
51                 this, LLDP_FLOOD_PERIOD, LLDP_FLOOD_PERIOD, TimeUnit.SECONDS);
52         this.packetProcessingService = packetProcessingService;
53         LOG.info("LLDPSpeaker started, it will send LLDP frames each {} seconds", LLDP_FLOOD_PERIOD);
54     }
55
56     /**
57      * Closes this resource, relinquishing any underlying resources.
58      */
59     @Override
60     public void close() {
61         nodeConnectorMap.clear();
62         scheduledExecutorService.shutdown();
63         scheduledSpeakerTask.cancel(true);
64         LOG.trace("LLDPSpeaker stopped sending LLDP frames.");
65     }
66
67     /**
68      * Send LLDPDU frames to all known openflow switch ports.
69      */
70     @Override
71     public void run() {
72         LOG.debug("Sending LLDP frames to {} ports...", nodeConnectorMap.keySet().size());
73
74         for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap.keySet()) {
75             NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
76             LOG.trace("Sending LLDP through port {}", nodeConnectorId.getValue());
77             packetProcessingService.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId));
78         }
79     }
80
81     /**
82      * {@inheritDoc}
83      */
84     @Override
85     public void nodeConnectorAdded(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
86                                    FlowCapableNodeConnector flowConnector) {
87         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
88
89         // nodeConnectorAdded can be called even if we already sending LLDP frames to
90         // port, so first we check if we actually need to perform any action
91         if (nodeConnectorMap.containsKey(nodeConnectorInstanceId)) {
92             LOG.trace("Port {} already in LLDPSpeaker.nodeConnectorMap, no need for additional processing",
93                     nodeConnectorId.getValue());
94             return;
95         }
96
97         // Prepare to build LLDP payload
98         InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class);
99         NodeId nodeId = InstanceIdentifier.keyOf(nodeInstanceId).getId();
100         MacAddress srcMacAddress = flowConnector.getHardwareAddress();
101         Long outputPortNo = flowConnector.getPortNumber().getUint32();
102
103         // No need to send LLDP frames on local ports
104         if (outputPortNo == null) {
105             LOG.trace("Port {} is local, not sending LLDP frames through it",
106                     nodeConnectorId.getValue());
107             return;
108         }
109
110         // Generate packet with destination switch and port
111         TransmitPacketInput packet = new TransmitPacketInputBuilder()
112                 .setEgress(new NodeConnectorRef(nodeConnectorInstanceId))
113                 .setNode(new NodeRef(nodeInstanceId))
114                 .setPayload(LLDPUtil.buildLldpFrame(nodeId, nodeConnectorId, srcMacAddress, outputPortNo))
115                 .build();
116
117         // Save packet to node connector id -> packet map to transmit it every 5 seconds
118         nodeConnectorMap.put(nodeConnectorInstanceId, packet);
119         LOG.trace("Port {} added to LLDPSpeaker.nodeConnectorMap", nodeConnectorId.getValue());
120
121         // Transmit packet for first time immediately
122         packetProcessingService.transmitPacket(packet);
123     }
124
125     /**
126      * {@inheritDoc}
127      */
128     @Override
129     public void nodeConnectorRemoved(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) {
130         nodeConnectorMap.remove(nodeConnectorInstanceId);
131         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
132         LOG.trace("Port {} removed from LLDPSpeaker.nodeConnectorMap", nodeConnectorId.getValue());
133     }
134 }