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