Merge "Bug 5921 EOS inJeopardy flag"
[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.NodeConnectorId;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.lldp.speaker.rev141023.OperStatus;
29 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * Objects of this class send LLDP frames over all flow-capable ports that can
35  * be discovered through inventory.
36  */
37 public class LLDPSpeaker implements AutoCloseable, NodeConnectorEventsObserver,
38         Runnable {
39
40     private static final Logger LOG = LoggerFactory
41             .getLogger(LLDPSpeaker.class);
42     private static final long LLDP_FLOOD_PERIOD = 5;
43
44     private final PacketProcessingService packetProcessingService;
45     private final ScheduledExecutorService scheduledExecutorService;
46     private final Map<InstanceIdentifier<NodeConnector>, TransmitPacketInput> nodeConnectorMap = new ConcurrentHashMap<>();
47     private final ScheduledFuture<?> scheduledSpeakerTask;
48     private final MacAddress addressDestionation;
49     private volatile OperStatus operationalStatus = OperStatus.RUN;
50
51     public LLDPSpeaker(final PacketProcessingService packetProcessingService,
52             final MacAddress addressDestionation) {
53         this(packetProcessingService, Executors
54                 .newSingleThreadScheduledExecutor(), addressDestionation);
55     }
56
57     public void setOperationalStatus(final OperStatus operationalStatus) {
58         LOG.info("Setting operational status to {}", operationalStatus);
59         this.operationalStatus = operationalStatus;
60         if (operationalStatus.equals(OperStatus.STANDBY)) {
61             nodeConnectorMap.clear();
62         }
63     }
64
65     public OperStatus getOperationalStatus() {
66         return operationalStatus;
67     }
68
69     public LLDPSpeaker(final PacketProcessingService packetProcessingService,
70             final ScheduledExecutorService scheduledExecutorService,
71             final MacAddress addressDestionation) {
72         this.addressDestionation = addressDestionation;
73         this.scheduledExecutorService = scheduledExecutorService;
74         scheduledSpeakerTask = this.scheduledExecutorService
75                 .scheduleAtFixedRate(this, LLDP_FLOOD_PERIOD,
76                         LLDP_FLOOD_PERIOD, TimeUnit.SECONDS);
77         this.packetProcessingService = packetProcessingService;
78         LOG.info(
79                 "LLDPSpeaker started, it will send LLDP frames each {} seconds",
80                 LLDP_FLOOD_PERIOD);
81     }
82
83     /**
84      * Closes this resource, relinquishing any underlying resources.
85      */
86     @Override
87     public void close() {
88         nodeConnectorMap.clear();
89         scheduledExecutorService.shutdown();
90         scheduledSpeakerTask.cancel(true);
91         LOG.trace("LLDPSpeaker stopped sending LLDP frames.");
92     }
93
94     /**
95      * Send LLDPDU frames to all known openflow switch ports.
96      */
97     @Override
98     public void run() {
99         if (OperStatus.RUN.equals(operationalStatus)) {
100             LOG.debug("Sending LLDP frames to {} ports...", nodeConnectorMap
101                     .keySet().size());
102
103             for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap
104                     .keySet()) {
105                 NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(
106                         nodeConnectorInstanceId).getId();
107                 LOG.trace("Sending LLDP through port {}",
108                         nodeConnectorId.getValue());
109                 packetProcessingService.transmitPacket(nodeConnectorMap
110                         .get(nodeConnectorInstanceId));
111             }
112         }
113     }
114
115     /**
116      * {@inheritDoc}
117      */
118     @Override
119     public void nodeConnectorAdded(
120             final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
121             final FlowCapableNodeConnector flowConnector) {
122         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(
123                 nodeConnectorInstanceId).getId();
124
125         // nodeConnectorAdded can be called even if we already sending LLDP
126         // frames to
127         // port, so first we check if we actually need to perform any action
128         if (nodeConnectorMap.containsKey(nodeConnectorInstanceId)) {
129             LOG.trace(
130                     "Port {} already in LLDPSpeaker.nodeConnectorMap, no need for additional processing",
131                     nodeConnectorId.getValue());
132             return;
133         }
134
135         // Prepare to build LLDP payload
136         InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId
137                 .firstIdentifierOf(Node.class);
138         NodeId nodeId = InstanceIdentifier.keyOf(nodeInstanceId).getId();
139         MacAddress srcMacAddress = flowConnector.getHardwareAddress();
140         Long outputPortNo = flowConnector.getPortNumber().getUint32();
141
142         // No need to send LLDP frames on local ports
143         if (outputPortNo == null) {
144             LOG.trace("Port {} is local, not sending LLDP frames through it",
145                     nodeConnectorId.getValue());
146             return;
147         }
148
149         // Generate packet with destination switch and port
150         TransmitPacketInput packet = new TransmitPacketInputBuilder()
151                 .setEgress(new NodeConnectorRef(nodeConnectorInstanceId))
152                 .setNode(new NodeRef(nodeInstanceId))
153                 .setPayload(
154                         LLDPUtil.buildLldpFrame(nodeId, nodeConnectorId,
155                                 srcMacAddress, outputPortNo,
156                                 addressDestionation)).build();
157
158         // Save packet to node connector id -> packet map to transmit it every 5
159         // seconds
160         nodeConnectorMap.put(nodeConnectorInstanceId, packet);
161         LOG.trace("Port {} added to LLDPSpeaker.nodeConnectorMap",
162                 nodeConnectorId.getValue());
163
164         // Transmit packet for first time immediately
165         packetProcessingService.transmitPacket(packet);
166     }
167
168     /**
169      * {@inheritDoc}
170      */
171     @Override
172     public void nodeConnectorRemoved(
173             final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) {
174         nodeConnectorMap.remove(nodeConnectorInstanceId);
175         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(
176                 nodeConnectorInstanceId).getId();
177         LOG.trace("Port {} removed from LLDPSpeaker.nodeConnectorMap",
178                 nodeConnectorId.getValue());
179     }
180 }