LLDPSpeaker - Periodically sends out LLDP packets on interfaces 23/3823/3
authorEd Warnicke <eaw@cisco.com>
Sat, 14 Dec 2013 23:04:16 +0000 (15:04 -0800)
committerEd Warnicke <eaw@cisco.com>
Thu, 19 Dec 2013 13:58:16 +0000 (05:58 -0800)
Change-Id: I8ce84e7db45edd1b8b4708831765bb48b3944b40
Signed-off-by: Ed Warnicke <eaw@cisco.com>
openflowplugin/pom.xml
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/MDController.java
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/sal/ModelDrivenSwitchImpl.java
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/sal/SalRegistrationManager.java
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeaker.java [new file with mode: 0644]
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeakerPopListener.java [new file with mode: 0644]
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/util/InventoryDataServiceUtil.java

index 444751e7506914c9c840e2950e73f94d0ce8494c..e1447e7cb8634593fd85ce8ddc2dff4985e5f9de 100644 (file)
@@ -42,7 +42,7 @@
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>sal</artifactId>
-            <version>0.5.1-SNAPSHOT</version>
+            <version>0.7.0-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <dependency>
+         <groupId>org.apache.commons</groupId>
+         <artifactId>commons-lang3</artifactId>
+         <version>3.1</version>
+       </dependency>
         <dependency>
             <groupId>equinoxSDK381</groupId>
             <artifactId>org.eclipse.osgi</artifactId>
index 25eb19aa17dff5b313086f48a7dbd55a88a371f3..c89b27c748f2e8cd0df09b94c6c2923bb9bd4468 100644 (file)
@@ -32,6 +32,7 @@ import org.opendaylight.openflowplugin.openflow.md.core.translator.MultipartRepl
 import org.opendaylight.openflowplugin.openflow.md.core.translator.MultipartReplyTranslator;
 import org.opendaylight.openflowplugin.openflow.md.core.translator.PacketInTranslator;
 import org.opendaylight.openflowplugin.openflow.md.core.translator.PortStatusMessageToNodeConnectorUpdatedTranslator;
+import org.opendaylight.openflowplugin.openflow.md.lldp.LLDPSpeakerPopListener;
 import org.opendaylight.openflowplugin.openflow.md.queue.PopListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved;
@@ -144,6 +145,10 @@ public class MDController implements IMDController {
         //Notification registration for queue-statistics
         addMessagePopListener(QueueStatisticsUpdate.class, notificationPopListener);
 
+        //Notification for LLDPSpeaker
+        LLDPSpeakerPopListener<DataObject> lldpPopListener  = new LLDPSpeakerPopListener<DataObject>();
+        addMessagePopListener(NodeConnectorUpdated.class,lldpPopListener);
+        
         // Push the updated Listeners to Session Manager which will be then picked up by ConnectionConductor eventually
         OFSessionUtil.getSessionManager().setTranslatorMapping(messageTranslators);
         OFSessionUtil.getSessionManager().setPopListenerMapping(popListeners);
index df0592cd286ba461861fb7c81e7a51d727f9d2b6..e2e8184fd16c322320b5bee90290e21249e1a814 100644 (file)
@@ -428,7 +428,7 @@ public class ModelDrivenSwitchImpl extends AbstractModelDrivenSwitch {
 
     @Override
     public Future<RpcResult<Void>> transmitPacket(TransmitPacketInput input) {
-        // TODO Auto-generated method stub
+        LOG.info("TransmitPacket - {}",input);
         return null;
     }
 
index 17bd6dadda1ae2ce74d56afcf22c5d43722d1184..0ee4f984fc18428ed5ca0d2a4719b416204e1d16 100644 (file)
@@ -21,6 +21,7 @@ import org.opendaylight.openflowplugin.openflow.md.core.session.OFSessionUtil;
 import org.opendaylight.openflowplugin.openflow.md.core.session.SessionContext;
 import org.opendaylight.openflowplugin.openflow.md.core.session.SessionListener;
 import org.opendaylight.openflowplugin.openflow.md.core.session.SessionManager;
+import org.opendaylight.openflowplugin.openflow.md.lldp.LLDPSpeaker;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdated;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdatedBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
@@ -91,6 +92,7 @@ public class SalRegistrationManager implements SessionListener, SwitchInventory
         NodeRef nodeRef = new NodeRef(identifier);
         NodeId nodeId = nodeIdFromDatapathId(datapathId);
         ModelDrivenSwitchImpl ofSwitch = new ModelDrivenSwitchImpl(nodeId, identifier, context);
+        LLDPSpeaker.getInstance().addModelDrivenSwitch(identifier, ofSwitch);
         salSwitches.put(identifier, ofSwitch);
         ofSwitch.register(providerContext);
 
@@ -106,7 +108,7 @@ public class SalRegistrationManager implements SessionListener, SwitchInventory
         InstanceIdentifier<Node> identifier = identifierFromDatapathId(datapathId);
         NodeRef nodeRef = new NodeRef(identifier);
         NodeRemoved nodeRemoved = nodeRemoved(nodeRef);
-
+        LLDPSpeaker.getInstance().removeModelDrivenSwitch(identifier);
         LOG.info("ModelDrivenSwitch for {} unregistred from MD-SAL.", datapathId.toString());
         publishService.publish(nodeRemoved);
     }
diff --git a/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeaker.java b/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeaker.java
new file mode 100644 (file)
index 0000000..0488a7d
--- /dev/null
@@ -0,0 +1,166 @@
+package org.opendaylight.openflowplugin.openflow.md.lldp;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.lang3.StringUtils;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.LLDP;
+import org.opendaylight.controller.sal.packet.LLDPTLV;
+import org.opendaylight.controller.sal.packet.PacketException;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.openflowplugin.openflow.md.ModelDrivenSwitch;
+import org.opendaylight.openflowplugin.openflow.md.util.InventoryDataServiceUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class LLDPSpeaker {
+    private static Logger LOG = LoggerFactory.getLogger(LLDPSpeaker.class);
+       
+       private  final Map<InstanceIdentifier<NodeConnector>,TransmitPacketInput> nodeConnectorMap = new ConcurrentHashMap<InstanceIdentifier<NodeConnector>,TransmitPacketInput>();
+       private  final Map<InstanceIdentifier<Node>,ModelDrivenSwitch> nodeMap = new ConcurrentHashMap<InstanceIdentifier<Node>,ModelDrivenSwitch>();
+       private static final LLDPSpeaker instance = new LLDPSpeaker();
+       private Timer timer = new Timer();
+       private static final int DELAY = 0;
+       private static final int PERIOD = 1000*5;
+       
+       private LLDPSpeaker() {
+           timer.schedule(new LLDPSpeakerTask(), DELAY, PERIOD);
+       }
+       
+       public static LLDPSpeaker getInstance() {
+           return instance;
+       }
+       
+       public  void addModelDrivenSwitch(InstanceIdentifier<Node> nodeInstanceId, ModelDrivenSwitch sw) {
+               nodeMap.put(nodeInstanceId,sw);         
+       }
+       
+       public void removeModelDrivenSwitch(InstanceIdentifier<Node> nodeInstanceId) {
+           nodeMap.remove(nodeInstanceId);
+           for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap.keySet()) {
+               if(nodeInstanceId.equals(nodeConnectorInstanceId.firstIdentifierOf(Node.class))) {
+                   nodeConnectorMap.remove(nodeConnectorInstanceId);
+               }
+           }
+       }
+
+       public  void addNodeConnector(InstanceIdentifier<NodeConnector> nodeConnectorInstanceId, NodeConnector nodeConnector) {
+       InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class);
+       NodeKey nodeKey = InstanceIdentifier.keyOf(nodeInstanceId);
+       NodeId nodeId = nodeKey.getId();
+       NodeConnectorId nodeConnectorId = nodeConnector.getId();
+       FlowCapableNodeConnector flowConnector = nodeConnector.<FlowCapableNodeConnector>getAugmentation(FlowCapableNodeConnector.class);
+       TransmitPacketInputBuilder tpib = new TransmitPacketInputBuilder();
+       tpib.setEgress(new NodeConnectorRef(nodeConnectorInstanceId));
+       tpib.setNode(new NodeRef(nodeInstanceId));
+       tpib.setPayload(lldpDataFrom(nodeInstanceId,nodeConnectorInstanceId,flowConnector.getHardwareAddress()));
+       nodeConnectorMap.put(nodeConnectorInstanceId, tpib.build());
+        ModelDrivenSwitch md = nodeMap.get(nodeInstanceId);
+        md.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId));
+       }
+
+       public  void removeNodeConnector(
+                       InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,
+                       NodeConnector nodeConnector) {
+               nodeConnectorMap.remove(nodeConnectorInstanceId);               
+       }
+       
+       private  byte[] lldpDataFrom(InstanceIdentifier<Node> nodeInstanceId,InstanceIdentifier<NodeConnector> nodeConnectorInstanceId,MacAddress src) {
+               
+           NodeId nodeId = InstanceIdentifier.keyOf(nodeInstanceId).getId();
+           NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
+               // Create LLDP TTL TLV
+        byte[] ttl = new byte[] { (byte) 0, (byte) 120 };
+        LLDPTLV ttlTlv = new LLDPTLV();
+        ttlTlv.setType(LLDPTLV.TLVType.TTL.getValue()).setLength((short) ttl.length).setValue(ttl);
+               
+        // Create LLDP ChassisID TLV
+        byte[] cidValue = LLDPTLV.createChassisIDTLVValue(colonize(StringUtils.leftPad(Long.toHexString(InventoryDataServiceUtil.dataPathIdFromNodeId(nodeId)),16,"0")));
+        LLDPTLV chassisIdTlv = new LLDPTLV();
+        chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue());
+        chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue()).setLength((short) cidValue.length)
+                .setValue(cidValue);
+
+        // Create LLDP SystemName TLV
+        byte[] snValue = LLDPTLV.createSystemNameTLVValue(nodeId.getValue());
+        LLDPTLV systemNameTlv = new LLDPTLV();
+        systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue());
+        systemNameTlv.setType(LLDPTLV.TLVType.SystemName.getValue()).setLength((short) snValue.length)
+                .setValue(snValue);
+
+        // Create LLDP PortID TL
+        Long portNo = InventoryDataServiceUtil.portNumberfromNodeConnectorId(nodeConnectorId);
+        String hexString = Long.toHexString(portNo);
+        byte[] pidValue = LLDPTLV.createPortIDTLVValue(hexString);
+        LLDPTLV portIdTlv = new LLDPTLV();
+        portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue()).setLength((short) pidValue.length).setValue(pidValue);
+        portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue());
+
+        // Create LLDP Custom TLV
+        NodeConnectorRef ncref = new NodeConnectorRef(nodeConnectorInstanceId);
+        byte[] customValue = LLDPTLV.createCustomTLVValue(ncref.toString());
+        LLDPTLV customTlv = new LLDPTLV();
+        customTlv.setType(LLDPTLV.TLVType.Custom.getValue()).setLength((short) customValue.length)
+                .setValue(customValue);
+
+        // Create LLDP Custom Option list
+        List<LLDPTLV> customList = new ArrayList<LLDPTLV>();
+        customList.add(customTlv);
+
+        // Create discovery pkt
+        LLDP discoveryPkt = new LLDP();
+        discoveryPkt.setChassisId(chassisIdTlv).setPortId(portIdTlv).setTtl(ttlTlv).setSystemNameId(systemNameTlv)
+                .setOptionalTLVList(customList);
+
+        // Create ethernet pkt
+        byte[] sourceMac = HexEncode.bytesFromHexString(src.getValue());
+        Ethernet ethPkt = new Ethernet();
+        ethPkt.setSourceMACAddress(sourceMac).setDestinationMACAddress(LLDP.LLDPMulticastMac)
+                .setEtherType(EtherTypes.LLDP.shortValue()).setPayload(discoveryPkt);
+
+        try {
+            byte[] data = ethPkt.serialize();
+            return data;
+        } catch (PacketException e) {
+            LOG.error("Error creating LLDP packet",e);
+        }
+        return null;
+       }
+       
+       private class LLDPSpeakerTask extends TimerTask {
+
+        @Override
+        public void run() {
+            for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap.keySet()) {
+                InstanceIdentifier<Node> nodeInstanceId = nodeConnectorInstanceId.firstIdentifierOf(Node.class);
+                ModelDrivenSwitch md = nodeMap.get(nodeInstanceId);
+                md.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId));
+            }
+            
+        }
+           
+       }
+       
+       private String colonize(String orig) {
+           return orig.replaceAll("(?<=..)(..)", ":$1");
+       }
+}
diff --git a/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeakerPopListener.java b/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/lldp/LLDPSpeakerPopListener.java
new file mode 100644 (file)
index 0000000..abc6503
--- /dev/null
@@ -0,0 +1,38 @@
+package org.opendaylight.openflowplugin.openflow.md.lldp;
+
+import org.opendaylight.openflowplugin.openflow.md.queue.PopListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class LLDPSpeakerPopListener<T> implements PopListener<T> {
+
+
+    @Override
+    public void onPop(T processedMessage) {
+        if(processedMessage instanceof NodeConnectorUpdated) {
+               NodeConnectorUpdated connector = (NodeConnectorUpdated) processedMessage;
+               FlowCapableNodeConnectorUpdated flowConnector = connector.<FlowCapableNodeConnectorUpdated>getAugmentation(FlowCapableNodeConnectorUpdated.class);
+               if(flowConnector != null) {
+                       InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = (InstanceIdentifier<NodeConnector>) connector.getNodeConnectorRef().getValue();             
+                       NodeConnectorBuilder ncb = new NodeConnectorBuilder(connector);
+                       FlowCapableNodeConnectorBuilder fcncb = new FlowCapableNodeConnectorBuilder(flowConnector);
+                       ncb.addAugmentation(FlowCapableNodeConnector.class, fcncb.build());
+                       PortState portState = flowConnector.getState();
+                       PortConfig portConfig = flowConnector.getConfiguration();
+                       if((portState ==null || !portState.equals(PortState.LinkDown)) && (portConfig == null || !portConfig.isPORTDOWN())) {
+                               LLDPSpeaker.getInstance().addNodeConnector(nodeConnectorInstanceId,ncb.build());
+                       } else {
+                               LLDPSpeaker.getInstance().removeNodeConnector(nodeConnectorInstanceId,ncb.build());
+                       }
+               }
+        } 
+    }
+
+}
index fb6532c71fc72681d4f36170340fddad1f345cf7..3e80c812ee4421e4deca38e4a11332203f45a145 100644 (file)
@@ -122,6 +122,12 @@ public class InventoryDataServiceUtil {
         String current = datapathId.toString();
         return new NodeId(OF_URI_PREFIX + current);
     }
+    
+    public static Long dataPathIdFromNodeId(NodeId nodeId) {
+        String dpids = nodeId.getValue().replace(OF_URI_PREFIX, "");
+        Long dpid = Long.decode(dpids);
+        return dpid;
+    }
 
     public static NodeRef nodeRefFromNode(Node node) {
         return nodeRefFromNodeKey(node.getKey());
@@ -145,7 +151,9 @@ public class InventoryDataServiceUtil {
     
     public static Long portNumberfromNodeConnectorId(NodeConnectorId ncId) {
         String[] split = ncId.getValue().split(":");
-        return Long.getLong(split[split.length-1]);
+        String portNoString = split[split.length-1];
+        Long portNo = Long.decode(portNoString);
+        return portNo;
     }