Fix findbugs violations in applications
[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 com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.ThreadFactoryBuilder;
13 import java.security.NoSuchAlgorithmException;
14 import java.util.Arrays;
15 import java.util.Map;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.Executors;
18 import java.util.concurrent.Future;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.ThreadFactory;
22 import java.util.concurrent.TimeUnit;
23 import java.util.concurrent.atomic.AtomicInteger;
24 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
25 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService;
26 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.lldp.speaker.rev141023.OperStatus;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.opendaylight.yangtools.yang.common.RpcResult;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46  * Objects of this class send LLDP frames over all flow-capable ports that can
47  * be discovered through inventory.
48  */
49 public class LLDPSpeaker implements NodeConnectorEventsObserver, Runnable, AutoCloseable {
50     private static final Logger LOG = LoggerFactory.getLogger(LLDPSpeaker.class);
51
52     private static final long LLDP_FLOOD_PERIOD = 5;
53     private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
54             .setNameFormat("lldp-speaker-%d").setDaemon(true).build();
55     private final PacketProcessingService packetProcessingService;
56     private final ScheduledExecutorService scheduledExecutorService;
57     private final DeviceOwnershipStatusService deviceOwnershipStatusService;
58     private final Map<InstanceIdentifier<NodeConnector>, TransmitPacketInput> nodeConnectorMap =
59             new ConcurrentHashMap<>();
60     private final MacAddress addressDestionation;
61     private long currentFloodPeriod = LLDP_FLOOD_PERIOD;
62     private ScheduledFuture<?> scheduledSpeakerTask;
63     private volatile OperStatus operationalStatus = OperStatus.RUN;
64
65     public LLDPSpeaker(final PacketProcessingService packetProcessingService, final MacAddress addressDestionation,
66                        final EntityOwnershipService entityOwnershipService) {
67         this(packetProcessingService, Executors.newSingleThreadScheduledExecutor(THREAD_FACTORY), addressDestionation,
68                 entityOwnershipService);
69     }
70
71     public LLDPSpeaker(final PacketProcessingService packetProcessingService,
72                        final ScheduledExecutorService scheduledExecutorService,
73                        final MacAddress addressDestionation,
74                        final EntityOwnershipService entityOwnershipService) {
75         this.addressDestionation = addressDestionation;
76         this.scheduledExecutorService = scheduledExecutorService;
77         this.deviceOwnershipStatusService = new DeviceOwnershipStatusService(entityOwnershipService);
78         scheduledSpeakerTask = this.scheduledExecutorService
79                 .scheduleAtFixedRate(this, LLDP_FLOOD_PERIOD,LLDP_FLOOD_PERIOD, TimeUnit.SECONDS);
80         this.packetProcessingService = packetProcessingService;
81         LOG.info("LLDPSpeaker started, it will send LLDP frames each {} seconds", LLDP_FLOOD_PERIOD);
82     }
83
84     public void setOperationalStatus(final OperStatus operationalStatus) {
85         LOG.info("LLDP speaker operational status set to {}", operationalStatus);
86         this.operationalStatus = operationalStatus;
87         if (operationalStatus.equals(OperStatus.STANDBY)) {
88             nodeConnectorMap.clear();
89         }
90     }
91
92     public OperStatus getOperationalStatus() {
93         return operationalStatus;
94     }
95
96     public void setLldpFloodInterval(long time) {
97         this.currentFloodPeriod = time;
98         scheduledSpeakerTask.cancel(false);
99         scheduledSpeakerTask = this.scheduledExecutorService
100                      .scheduleAtFixedRate(this, time, time, TimeUnit.SECONDS);
101         LOG.info("LLDPSpeaker restarted, it will send LLDP frames each {} seconds", time);
102     }
103
104     public long getLldpFloodInterval() {
105         return currentFloodPeriod;
106     }
107
108     /**
109      * Closes this resource, relinquishing any underlying resources.
110      */
111     @Override
112     public void close() {
113         nodeConnectorMap.clear();
114         if (scheduledExecutorService != null) {
115             scheduledExecutorService.shutdown();
116         }
117         if (scheduledSpeakerTask != null) {
118             scheduledSpeakerTask.cancel(true);
119         }
120         LOG.trace("LLDPSpeaker stopped sending LLDP frames.");
121     }
122
123     /**
124      * Send LLDPDU frames to all known openflow switch ports.
125      */
126     @Override
127     public void run() {
128         if (OperStatus.RUN.equals(operationalStatus)) {
129             LOG.debug("Sending LLDP frames to nodes {}", Arrays.toString(deviceOwnershipStatusService
130                     .getOwnedNodes().toArray()));
131             LOG.debug("Sending LLDP frames to total {} ports", getOwnedPorts());
132             nodeConnectorMap.keySet().forEach(ncIID -> {
133                 NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(ncIID).getId();
134                 NodeId nodeId = ncIID.firstKeyOf(Node.class, NodeKey.class).getId();
135                 if (deviceOwnershipStatusService.isEntityOwned(nodeId.getValue())) {
136                     LOG.debug("Node is owned by this controller, sending LLDP packet through port {}",
137                             nodeConnectorId.getValue());
138                     packetProcessingService.transmitPacket(nodeConnectorMap.get(ncIID));
139                 } else {
140                     LOG.trace("Node {} is not owned by this controller, so skip sending LLDP packet on port {}",
141                             nodeId.getValue(), nodeConnectorId.getValue());
142                 }
143             });
144         }
145     }
146
147     @Override
148     public void nodeConnectorAdded(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
149                                    final FlowCapableNodeConnector flowConnector) {
150         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
151
152         // nodeConnectorAdded can be called even if we already sending LLDP
153         // frames to
154         // port, so first we check if we actually need to perform any action
155         if (nodeConnectorMap.containsKey(nodeConnectorInstanceId)) {
156             LOG.debug("Port {} already in LLDPSpeaker.nodeConnectorMap, no need for additional processing",
157                     nodeConnectorId.getValue());
158             return;
159         }
160
161         // Prepare to build LLDP payload
162         InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class);
163         NodeId nodeId = InstanceIdentifier.keyOf(nodeInstanceId).getId();
164         MacAddress srcMacAddress = flowConnector.getHardwareAddress();
165         Long outputPortNo = flowConnector.getPortNumber().getUint32();
166
167         // No need to send LLDP frames on local ports
168         if (outputPortNo == null) {
169             LOG.debug("Port {} is local, not sending LLDP frames through it", nodeConnectorId.getValue());
170             return;
171         }
172
173         // Generate packet with destination switch and port
174         TransmitPacketInput packet;
175         try {
176             packet = new TransmitPacketInputBuilder()
177                     .setEgress(new NodeConnectorRef(nodeConnectorInstanceId))
178                     .setNode(new NodeRef(nodeInstanceId)).setPayload(LLDPUtil.buildLldpFrame(nodeId,
179                             nodeConnectorId, srcMacAddress, outputPortNo, addressDestionation)).build();
180         } catch (NoSuchAlgorithmException | PacketException e) {
181             LOG.error("Error building LLDP frame", e);
182             return;
183         }
184
185         // Save packet to node connector id -> packet map to transmit it periodically on the configured interval.
186         nodeConnectorMap.put(nodeConnectorInstanceId, packet);
187         LOG.debug("Port {} added to LLDPSpeaker.nodeConnectorMap", nodeConnectorId.getValue());
188
189         // Transmit packet for first time immediately
190         final Future<RpcResult<Void>> resultFuture = packetProcessingService.transmitPacket(packet);
191         JdkFutures.addErrorLogging(resultFuture, LOG, "transmitPacket");
192     }
193
194     @Override
195     public void nodeConnectorRemoved(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) {
196         Preconditions.checkNotNull(nodeConnectorInstanceId);
197
198         nodeConnectorMap.remove(nodeConnectorInstanceId);
199         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
200         LOG.trace("Port removed from node-connector map : {}", nodeConnectorId.getValue());
201     }
202
203     private int getOwnedPorts() {
204         AtomicInteger ownedPorts = new AtomicInteger();
205         nodeConnectorMap.keySet().forEach(ncIID -> {
206             NodeId nodeId = ncIID.firstKeyOf(Node.class, NodeKey.class).getId();
207             if (deviceOwnershipStatusService.isEntityOwned(nodeId.getValue())) {
208                 ownedPorts.incrementAndGet();
209             }
210         });
211         return ownedPorts.get();
212     }
213 }