Bug 1764 - moved Session related interfaces to openflowplugin-api
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / lldp / LLDPSpeaker.java
1 /**
2  * Copyright (c) 2013 Cisco Systems, Inc. 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 package org.opendaylight.openflowplugin.openflow.md.lldp;
9
10 import java.math.BigInteger;
11 import java.util.Collections;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Timer;
15 import java.util.TimerTask;
16 import java.util.concurrent.ConcurrentHashMap;
17 import org.opendaylight.controller.liblldp.EtherTypes;
18 import org.opendaylight.controller.liblldp.Ethernet;
19 import org.opendaylight.controller.liblldp.HexEncode;
20 import org.opendaylight.controller.liblldp.LLDP;
21 import org.opendaylight.controller.liblldp.LLDPTLV;
22 import org.opendaylight.controller.liblldp.PacketException;
23 import org.opendaylight.openflowplugin.api.openflow.md.util.OpenflowVersion;
24 import org.opendaylight.openflowplugin.api.openflow.md.ModelDrivenSwitch;
25 import org.opendaylight.openflowplugin.openflow.md.util.InventoryDataServiceUtil;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.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.inventory.rev130819.nodes.NodeKey;
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.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42 public class LLDPSpeaker {
43     private static Logger LOG = LoggerFactory.getLogger(LLDPSpeaker.class);
44
45     private final Map<InstanceIdentifier<NodeConnector>, TransmitPacketInput> nodeConnectorMap = new ConcurrentHashMap<InstanceIdentifier<NodeConnector>, TransmitPacketInput>();
46     private final Map<InstanceIdentifier<Node>, ModelDrivenSwitch> nodeMap = new ConcurrentHashMap<InstanceIdentifier<Node>, ModelDrivenSwitch>();
47     private static final LLDPSpeaker instance = new LLDPSpeaker();
48     private Timer timer = new Timer();
49     private static final int DELAY = 0;
50     private static final int PERIOD = 1000 * 5;
51
52     private LLDPSpeaker() {
53         timer.schedule(new LLDPSpeakerTask(), DELAY, PERIOD);
54     }
55
56     public static LLDPSpeaker getInstance() {
57         return instance;
58     }
59
60     public void addModelDrivenSwitch(InstanceIdentifier<Node> nodeInstanceId, ModelDrivenSwitch sw) {
61         nodeMap.put(nodeInstanceId, sw);
62     }
63
64     public void removeModelDrivenSwitch(InstanceIdentifier<Node> nodeInstanceId) {
65         nodeMap.remove(nodeInstanceId);
66         for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap.keySet()) {
67             if (nodeInstanceId.equals(nodeConnectorInstanceId.firstIdentifierOf(Node.class))) {
68                 nodeConnectorMap.remove(nodeConnectorInstanceId);
69             }
70         }
71     }
72
73     public void addNodeConnector(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId, NodeConnector nodeConnector) {
74         InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class);
75         ModelDrivenSwitch md = nodeMap.get(nodeInstanceId);
76
77         NodeKey nodeKey = InstanceIdentifier.keyOf(nodeInstanceId);
78         NodeId nodeId = nodeKey.getId();
79         NodeConnectorId nodeConnectorId = nodeConnector.getId();
80         FlowCapableNodeConnector flowConnector = nodeConnector.<FlowCapableNodeConnector>getAugmentation(FlowCapableNodeConnector.class);
81         TransmitPacketInputBuilder tpib = new TransmitPacketInputBuilder();
82         tpib.setEgress(new NodeConnectorRef(nodeConnectorInstanceId));
83         tpib.setNode(new NodeRef(nodeInstanceId));
84         if(nodeInstanceId == null) {
85             LOG.warn("addNodeConnector(): nodeInstanceId should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
86         } else if (nodeConnectorInstanceId == null) {
87             LOG.warn("addNodeConnector(): nodeConnectorInstanceId should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
88         } else if (flowConnector == null) {
89             LOG.warn("addNodeConnector(): flowConnector should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
90         } else if (md == null) {
91             LOG.debug("addNodeConnector(): md is null, this usually means your switch disconnected while you had unprocessed NodeConnectorUpdated messages in queue nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
92         } else if(md.getSessionContext() == null) {
93             LOG.warn("addNodeConnector(): md.getSessionContext() should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
94         } else if (md.getSessionContext().getPrimaryConductor() == null) {
95             LOG.warn("addNodeConnector(): md.getSessionContext().getPrimaryConductor() should not be null nodeConnectorInstanceId {} nodeConnector {}",nodeConnectorInstanceId,nodeConnector);
96         } else {
97             tpib.setPayload(lldpDataFrom(nodeInstanceId,nodeConnectorInstanceId,flowConnector.getHardwareAddress(),
98                     md.getSessionContext().getPrimaryConductor().getVersion()));
99             nodeConnectorMap.put(nodeConnectorInstanceId, tpib.build());
100
101             md.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId));
102         }
103     }
104
105     public void removeNodeConnector(
106             InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
107             NodeConnector nodeConnector) {
108         nodeConnectorMap.remove(nodeConnectorInstanceId);
109     }
110
111     private byte[] lldpDataFrom(InstanceIdentifier<Node> nodeInstanceId, InstanceIdentifier<NodeConnector> nodeConnectorInstanceId, MacAddress src,
112                                 Short version) {
113
114         NodeId nodeId = InstanceIdentifier.keyOf(nodeInstanceId).getId();
115         NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
116         // Create LLDP TTL TLV
117         byte[] ttl = new byte[]{(byte) 0, (byte) 120};
118         LLDPTLV ttlTlv = new LLDPTLV();
119         ttlTlv.setType(LLDPTLV.TLVType.TTL.getValue()).setLength((short) ttl.length).setValue(ttl);
120
121         // Create LLDP ChassisID TLV
122         BigInteger dataPathId = InventoryDataServiceUtil.dataPathIdFromNodeId(nodeId);
123         byte[] cidValue = LLDPTLV.createChassisIDTLVValue(
124                 colonize(InventoryDataServiceUtil.bigIntegerToPaddedHex(dataPathId)));
125         LLDPTLV chassisIdTlv = new LLDPTLV();
126         chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue());
127         chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue()).setLength((short) cidValue.length)
128                 .setValue(cidValue);
129
130         // Create LLDP SystemName TLV
131         byte[] snValue = LLDPTLV.createSystemNameTLVValue(nodeId.getValue());
132         LLDPTLV systemNameTlv = new LLDPTLV();
133         systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue());
134         systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue()).setLength((short) snValue.length)
135                 .setValue(snValue);
136
137         // Create LLDP PortID TL
138         Long portNo = InventoryDataServiceUtil.portNumberfromNodeConnectorId(OpenflowVersion.get(version), nodeConnectorId);
139
140         String hexString = Long.toHexString(portNo);
141         byte[] pidValue = LLDPTLV.createPortIDTLVValue(hexString);
142         LLDPTLV portIdTlv = new LLDPTLV();
143         portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue()).setLength((short) pidValue.length).setValue(pidValue);
144         portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue());
145
146         // Create LLDP Custom TLV
147         byte[] customValue = LLDPTLV.createCustomTLVValue(nodeConnectorId.getValue());
148         LLDPTLV customTlv = new LLDPTLV();
149         customTlv.setType(LLDPTLV.TLVType.Custom.getValue()).setLength((short) customValue.length)
150                 .setValue(customValue);
151
152         // Create LLDP Custom Option list
153         List<LLDPTLV> customList = Collections.singletonList(customTlv);
154
155         // Create discovery pkt
156         LLDP discoveryPkt = new LLDP();
157         discoveryPkt.setChassisId(chassisIdTlv).setPortId(portIdTlv).setTtl(ttlTlv).setSystemNameId(systemNameTlv)
158                 .setOptionalTLVList(customList);
159
160         // Create ethernet pkt
161         byte[] sourceMac = HexEncode.bytesFromHexString(src.getValue());
162         Ethernet ethPkt = new Ethernet();
163         ethPkt.setSourceMACAddress(sourceMac).setDestinationMACAddress(LLDP.LLDPMulticastMac)
164                 .setEtherType(EtherTypes.LLDP.shortValue()).setPayload(discoveryPkt);
165
166         try {
167             byte[] data = ethPkt.serialize();
168             return data;
169         } catch (PacketException e) {
170             LOG.error("Error creating LLDP packet", e);
171         }
172         return null;
173     }
174
175     private class LLDPSpeakerTask extends TimerTask {
176
177         @Override
178         public void run() {
179             for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap.keySet()) {
180                 InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class);
181                 ModelDrivenSwitch md = nodeMap.get(nodeInstanceId);
182                 md.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId));
183             }
184
185         }
186
187     }
188
189     private String colonize(String orig) {
190         return orig.replaceAll("(?<=..)(..)", ":$1");
191     }
192 }