- Discard discovery packet if the ingress port is down
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / DiscoveryService.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
9 package org.opendaylight.controller.protocol_plugin.openflow.internal;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.Timer;
18 import java.util.TimerTask;
19 import java.util.concurrent.BlockingQueue;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.concurrent.CopyOnWriteArrayList;
23 import java.util.concurrent.LinkedBlockingQueue;
24
25 import org.eclipse.osgi.framework.console.CommandInterpreter;
26 import org.eclipse.osgi.framework.console.CommandProvider;
27 import org.opendaylight.controller.protocol_plugin.openflow.IDataPacketListen;
28 import org.opendaylight.controller.protocol_plugin.openflow.IDataPacketMux;
29 import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
30 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
31 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
32 import org.openflow.protocol.OFPhysicalPort;
33 import org.osgi.framework.BundleContext;
34 import org.osgi.framework.FrameworkUtil;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import org.opendaylight.controller.sal.core.Config;
39 import org.opendaylight.controller.sal.core.ConstructionException;
40 import org.opendaylight.controller.sal.core.Edge;
41 import org.opendaylight.controller.sal.core.ContainerFlow;
42 import org.opendaylight.controller.sal.core.IContainerListener;
43 import org.opendaylight.controller.sal.core.Node;
44 import org.opendaylight.controller.sal.core.NodeConnector;
45 import org.opendaylight.controller.sal.core.Property;
46 import org.opendaylight.controller.sal.core.State;
47 import org.opendaylight.controller.sal.core.UpdateType;
48 import org.opendaylight.controller.sal.discovery.IDiscoveryService;
49 import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
50 import org.opendaylight.controller.sal.packet.Ethernet;
51 import org.opendaylight.controller.sal.packet.LLDP;
52 import org.opendaylight.controller.sal.packet.LLDPTLV;
53 import org.opendaylight.controller.sal.packet.LinkEncap;
54 import org.opendaylight.controller.sal.packet.PacketResult;
55 import org.opendaylight.controller.sal.packet.RawPacket;
56 import org.opendaylight.controller.sal.utils.EtherTypes;
57 import org.opendaylight.controller.sal.utils.HexEncode;
58 import org.opendaylight.controller.sal.utils.NetUtils;
59 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
60 import org.opendaylight.controller.sal.utils.NodeCreator;
61
62 /**
63  * The class describes neighbor discovery service for an OpenFlow network.
64  */
65 public class DiscoveryService implements IInventoryShimExternalListener,
66         IDataPacketListen, IContainerListener, CommandProvider {
67     private static Logger logger = LoggerFactory
68             .getLogger(DiscoveryService.class);
69     private IController controller = null;
70     private IDiscoveryService discoveryService = null;
71     private IPluginInInventoryService pluginInInventoryService = null;
72     private IDataPacketMux iDataPacketMux = null;
73
74     private List<NodeConnector> readyListHi = null; // newly added ports go into
75                                                     // this list and will be
76                                                     // served first
77     private List<NodeConnector> readyListLo = null; // come here after served at
78                                                     // least once
79     private List<NodeConnector> waitingList = null; // staging area during quiet
80                                                     // period
81     private ConcurrentMap<NodeConnector, Integer> pendingMap = null;// wait for
82                                                                     // response
83                                                                     // back
84     private ConcurrentMap<NodeConnector, Edge> edgeMap = null; // openflow edges
85                                                                // keyed by head
86                                                                // connector
87     private ConcurrentMap<NodeConnector, Integer> agingMap = null; // aging
88                                                                    // entries
89                                                                    // keyed by
90                                                                    // edge port
91     private ConcurrentMap<NodeConnector, Edge> prodMap = null; // production
92                                                                // edges keyed by
93                                                                // edge port
94
95     private Timer discoveryTimer; // discovery timer
96     private DiscoveryTimerTask discoveryTimerTask; // timer task
97     private long discoveryTimerTick = 1L * 1000; // per tick in msec
98     private int discoveryTimerTickCount = 0; // main tick counter
99     private int discoveryBatchMaxPorts = 500; // max # of ports handled in one
100                                               // batch
101     private int discoveryBatchRestartTicks = getDiscoveryInterval(); // periodically
102                                                                      // restart
103                                                                      // batching
104                                                                      // process
105     private int discoveryBatchPausePeriod = 5; // pause for few secs
106     private int discoveryBatchPauseTicks = discoveryBatchRestartTicks
107             - discoveryBatchPausePeriod; // pause after this point
108     private int discoveryRetry = getDiscoveryRetry(); // number of retries after
109                                                       // initial timeout
110     private int discoveryTimeoutTicks = getDiscoveryTimeout(); // timeout in sec
111     private int discoveryAgeoutTicks = 120; // age out 2 min
112     private int discoveryConsistencyCheckMultiple = 2; // multiple of
113                                                        // discoveryBatchRestartTicks
114     private int discoveryConsistencyCheckTickCount = discoveryBatchPauseTicks; // CC
115                                                                                // tick
116                                                                                // counter
117     private int discoveryConsistencyCheckCallingTimes = 0; // # of times CC gets
118                                                            // called
119     private int discoveryConsistencyCheckCorrected = 0; // # of cases CC
120                                                         // corrected
121     private boolean discoveryConsistencyCheckEnabled = true;// enable or disable
122                                                             // CC
123     private boolean discoveryAgingEnabled = true; // enable or disable aging
124     private boolean discoverySnoopingEnabled = true; // global flag to enable or
125                                                      // disable LLDP snooping
126     private List<NodeConnector> discoverySnoopingDisableList; // the list of
127                                                               // ports that will
128                                                               // not do LLDP
129                                                               // snooping
130     private BlockingQueue<NodeConnector> transmitQ;
131     private Thread transmitThread;
132     private Boolean throttling = false; // if true, no more batching.
133     private volatile Boolean shuttingDown = false;
134
135     private LLDPTLV chassisIdTlv, portIdTlv, ttlTlv, customTlv;
136
137     class DiscoveryTransmit implements Runnable {
138         private final BlockingQueue<NodeConnector> transmitQ;
139
140         DiscoveryTransmit(BlockingQueue<NodeConnector> transmitQ) {
141             this.transmitQ = transmitQ;
142         }
143
144         public void run() {
145             while (true) {
146                 try {
147                     NodeConnector nodeConnector = transmitQ.take();
148                     RawPacket outPkt = createDiscoveryPacket(nodeConnector);
149                     sendDiscoveryPacket(nodeConnector, outPkt);
150                     nodeConnector = null;
151                 } catch (InterruptedException e1) {
152                     logger.warn("DiscoveryTransmit interupted", e1.getMessage());
153                     if (shuttingDown)
154                         return;
155                 } catch (Exception e2) {
156                     logger.error("", e2);
157                 }
158             }
159         }
160     }
161
162     class DiscoveryTimerTask extends TimerTask {
163         public void run() {
164             checkTimeout();
165             checkAging();
166             doConsistencyCheck();
167             doDiscovery();
168         }
169     }
170
171     private RawPacket createDiscoveryPacket(NodeConnector nodeConnector) {
172         String nodeId = HexEncode.longToHexString((Long) nodeConnector
173                 .getNode().getID());
174
175         // Create LLDP ChassisID TLV
176         byte[] cidValue = LLDPTLV.createChassisIDTLVValue(nodeId);
177         chassisIdTlv.setType((byte) LLDPTLV.TLVType.ChassisID.getValue())
178                 .setLength((short) cidValue.length).setValue(cidValue);
179
180         // Create LLDP PortID TLV
181         String portId = nodeConnector.getNodeConnectorIDString();
182         byte[] pidValue = LLDPTLV.createPortIDTLVValue(portId);
183         portIdTlv.setType((byte) LLDPTLV.TLVType.PortID.getValue())
184                 .setLength((short) pidValue.length).setValue(pidValue);
185
186         // Create LLDP Custom TLV
187         byte[] customValue = LLDPTLV.createCustomTLVValue(nodeConnector
188                 .toString());
189         customTlv.setType((byte) LLDPTLV.TLVType.Custom.getValue())
190                 .setLength((short) customValue.length).setValue(customValue);
191
192         // Create LLDP Custom Option list
193         List<LLDPTLV> customList = new ArrayList<LLDPTLV>();
194         customList.add(customTlv);
195
196         // Create discovery pkt
197         LLDP discoveryPkt = new LLDP();
198         discoveryPkt.setChassisId(chassisIdTlv).setPortId(portIdTlv)
199                 .setTtl(ttlTlv).setOptionalTLVList(customList);
200
201         RawPacket rawPkt = null;
202         try {
203             // Create ethernet pkt
204             byte[] sourceMac = getSouceMACFromNodeID(nodeId);
205             Ethernet ethPkt = new Ethernet();
206             ethPkt.setSourceMACAddress(sourceMac)
207                     .setDestinationMACAddress(LLDP.LLDPMulticastMac)
208                     .setEtherType(EtherTypes.LLDP.shortValue())
209                     .setPayload(discoveryPkt);
210
211             byte[] data = ethPkt.serialize();
212             rawPkt = new RawPacket(data);
213             rawPkt.setOutgoingNodeConnector(nodeConnector);
214         } catch (ConstructionException cex) {
215             logger.warn("RawPacket creation caught exception {}",
216                     cex.getMessage());
217         } catch (Exception e) {
218             logger.error("Failed to serialize the LLDP packet: " + e);
219         }
220
221         return rawPkt;
222     }
223
224     private void sendDiscoveryPacket(NodeConnector nodeConnector,
225             RawPacket outPkt) {
226         if (nodeConnector == null) {
227             logger.debug("Can not send discovery packet out since nodeConnector is null");
228             return;
229         }
230
231         if (outPkt == null) {
232             logger.debug("Can not send discovery packet out since outPkt is null");
233             return;
234         }
235
236         long sid = (Long) nodeConnector.getNode().getID();
237         ISwitch sw = controller.getSwitches().get(sid);
238
239         if (sw == null) {
240             logger.debug(
241                     "Can not send discovery packet out since switch {} is null",
242                     sid);
243             return;
244         }
245
246         if (!sw.isOperational()) {
247             logger.debug(
248                     "Can not send discovery packet out since switch {} is not operational",
249                     sw);
250             return;
251         }
252
253         if (this.iDataPacketMux == null) {
254             logger.debug("Can not send discovery packet out since DataPacket service is not available");
255             return;
256         }
257
258         logger.trace("Sending topology discovery pkt thru {}", nodeConnector);
259         this.iDataPacketMux.transmitDataPacket(outPkt);
260     }
261
262     @Override
263     public PacketResult receiveDataPacket(RawPacket inPkt) {
264         if (inPkt == null) {
265             logger.debug("Ignoring null packet");
266             return PacketResult.IGNORED;
267         }
268
269         byte[] data = inPkt.getPacketData();
270         if (data.length <= 0) {
271             logger.trace("Ignoring zero length packet");
272             return PacketResult.IGNORED;
273         }
274
275         if (!inPkt.getEncap().equals(LinkEncap.ETHERNET)) {
276             logger.trace("Ignoring non ethernet packet");
277             return PacketResult.IGNORED;
278         }
279
280         if (((Short) inPkt.getIncomingNodeConnector().getID())
281                 .equals(NodeConnector.SPECIALNODECONNECTORID)) {
282             logger.trace("Ignoring ethernet packet received on special port: "
283                     + inPkt.getIncomingNodeConnector().toString());
284             return PacketResult.IGNORED;
285         }
286
287         Ethernet ethPkt = new Ethernet();
288         try {
289             ethPkt.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
290         } catch (Exception e) {
291             logger.warn("Failed to decode LLDP packet from {}: {}",
292                     inPkt.getIncomingNodeConnector(), e);
293             return PacketResult.IGNORED;
294         }
295         if (ethPkt.getPayload() instanceof LLDP) {
296             NodeConnector dst = inPkt.getIncomingNodeConnector();
297             if (isEnabled(dst)) {
298                 if (!processDiscoveryPacket(dst, ethPkt)) {
299                     /* Snoop the discovery pkt if not generated from us */
300                     snoopDiscoveryPacket(dst, ethPkt);
301                 }
302                 return PacketResult.CONSUME;
303             }
304         }
305         return PacketResult.IGNORED;
306     }
307
308     /*
309      * Snoop incoming discovery frames generated by the production network
310      * neighbor switch
311      */
312     private void snoopDiscoveryPacket(NodeConnector dstNodeConnector,
313             Ethernet ethPkt) {
314         if (!this.discoverySnoopingEnabled
315                 || discoverySnoopingDisableList.contains(dstNodeConnector)) {
316             logger.trace(
317                     "Discarded received discovery packet on {} since snooping is turned off",
318                     dstNodeConnector);
319             return;
320         }
321
322         if ((dstNodeConnector == null) || (ethPkt == null)) {
323             logger.trace("Quit snooping discovery packet: Null node connector or packet");
324             return;
325         }
326
327         LLDP lldp = (LLDP) ethPkt.getPayload();
328
329         try {
330             String nodeId = LLDPTLV.getHexStringValue(lldp.getChassisId()
331                     .getValue(), lldp.getChassisId().getLength());
332             String portId = LLDPTLV.getStringValue(lldp.getPortId().getValue(),
333                     lldp.getPortId().getLength());
334             byte[] systemNameBytes = null;
335             // get system name if present in the LLDP pkt
336             for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
337                 if (lldptlv.getType() == LLDPTLV.TLVType.SystemName.getValue()) {
338                     systemNameBytes = lldptlv.getValue();
339                     break;
340                 }
341             }
342             String nodeName = (systemNameBytes == null) ? nodeId : new String(
343                     systemNameBytes);
344             Node srcNode = new Node(Node.NodeIDType.PRODUCTION, nodeName);
345             NodeConnector srcNodeConnector = NodeConnectorCreator
346                     .createNodeConnector(
347                             NodeConnector.NodeConnectorIDType.PRODUCTION,
348                             portId, srcNode);
349
350             Edge edge = null;
351             Set<Property> props = null;
352             edge = new Edge(srcNodeConnector, dstNodeConnector);
353             props = getProps(dstNodeConnector);
354
355             updateProdEdge(edge, props);
356         } catch (Exception e) {
357             logger.warn("Caught exception ", e);
358         }
359     }
360
361     /*
362      * Handle discovery frames generated by our controller
363      * 
364      * @return true if it's a success
365      */
366     private boolean processDiscoveryPacket(NodeConnector dstNodeConnector,
367             Ethernet ethPkt) {
368         if ((dstNodeConnector == null) || (ethPkt == null)) {
369             logger.trace("Ignoring processing of discovery packet: Null node connector or packet");
370             return false;
371         }
372
373         logger.trace("Handle discovery packet {} from {}", ethPkt,
374                 dstNodeConnector);
375
376         LLDP lldp = (LLDP) ethPkt.getPayload();
377
378         List<LLDPTLV> optionalTLVList = lldp.getOptionalTLVList();
379         if (optionalTLVList == null) {
380             logger.info("The discovery packet with null custom option from {}",
381                     dstNodeConnector);
382             return false;
383         }
384
385         Node srcNode = null;
386         NodeConnector srcNodeConnector = null;
387         for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
388             if (lldptlv.getType() == LLDPTLV.TLVType.Custom.getValue()) {
389                 String ncString = LLDPTLV.getCustomString(lldptlv.getValue(),
390                         lldptlv.getLength());
391                 srcNodeConnector = NodeConnector.fromString(ncString);
392                 if (srcNodeConnector != null) {
393                     srcNode = srcNodeConnector.getNode();
394                     /* Check if it's expected */
395                     if (isTracked(srcNodeConnector)) {
396                         break;
397                     } else {
398                         srcNode = null;
399                         srcNodeConnector = null;
400                     }
401                 }
402             }
403         }
404
405         if ((srcNode == null) || (srcNodeConnector == null)) {
406             logger.trace(
407                     "Received non-controller generated discovery packet from {}",
408                     dstNodeConnector);
409             return false;
410         }
411
412         // push it out to Topology
413         Edge edge = null;
414         Set<Property> props = null;
415         try {
416             edge = new Edge(srcNodeConnector, dstNodeConnector);
417             props = getProps(dstNodeConnector);
418         } catch (ConstructionException e) {
419             logger.error("Caught exception ", e);
420         }
421         addEdge(edge, props);
422
423         logger.trace("Received discovery packet for Edge {}", edge);
424
425         return true;
426     }
427
428     public Map<String, Property> getPropMap(NodeConnector nodeConnector) {
429         if (nodeConnector == null) {
430             return null;
431         }
432
433         if (pluginInInventoryService == null) {
434             return null;
435         }
436
437         Map<NodeConnector, Map<String, Property>> props = pluginInInventoryService
438                 .getNodeConnectorProps(false);
439         if (props == null) {
440             return null;
441         }
442
443         return props.get(nodeConnector);
444     }
445
446     public Property getProp(NodeConnector nodeConnector, String propName) {
447         Map<String, Property> propMap = getPropMap(nodeConnector);
448         if (propMap == null) {
449             return null;
450         }
451
452         Property prop = (Property) propMap.get(propName);
453         return prop;
454     }
455
456     public Set<Property> getProps(NodeConnector nodeConnector) {
457         Map<String, Property> propMap = getPropMap(nodeConnector);
458         if (propMap == null) {
459             return null;
460         }
461
462         Set<Property> props = new HashSet<Property>(propMap.values());
463         return props;
464     }
465
466     private boolean isEnabled(NodeConnector nodeConnector) {
467         if (nodeConnector == null) {
468             return false;
469         }
470
471         Config config = (Config) getProp(nodeConnector, Config.ConfigPropName);
472         State state = (State) getProp(nodeConnector, State.StatePropName);
473         return ((config != null) && (config.getValue() == Config.ADMIN_UP)
474                 && (state != null) && (state.getValue() == State.EDGE_UP));
475     }
476
477     private boolean isTracked(NodeConnector nodeConnector) {
478         if (readyListHi.contains(nodeConnector)) {
479             return true;
480         }
481
482         if (readyListLo.contains(nodeConnector)) {
483             return true;
484         }
485
486         if (pendingMap.keySet().contains(nodeConnector)) {
487             return true;
488         }
489
490         if (waitingList.contains(nodeConnector)) {
491             return true;
492         }
493
494         return false;
495     }
496
497     private Set<NodeConnector> getWorkingSet() {
498         Set<NodeConnector> workingSet = new HashSet<NodeConnector>();
499         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
500
501         for (NodeConnector nodeConnector : readyListHi) {
502             if (isOverLimit(workingSet.size())) {
503                 break;
504             }
505
506             workingSet.add(nodeConnector);
507             removeSet.add(nodeConnector);
508         }
509         readyListHi.removeAll(removeSet);
510
511         removeSet.clear();
512         for (NodeConnector nodeConnector : readyListLo) {
513             if (isOverLimit(workingSet.size())) {
514                 break;
515             }
516
517             workingSet.add(nodeConnector);
518             removeSet.add(nodeConnector);
519         }
520         readyListLo.removeAll(removeSet);
521
522         return workingSet;
523     }
524
525     private Boolean isOverLimit(int size) {
526         return ((size >= discoveryBatchMaxPorts) && !throttling);
527     }
528
529     private void addDiscovery() {
530         Map<Long, ISwitch> switches = controller.getSwitches();
531         Set<Long> sidSet = switches.keySet();
532         if (sidSet == null) {
533             return;
534         }
535         for (Long sid : sidSet) {
536             Node node = NodeCreator.createOFNode(sid);
537             addDiscovery(node);
538         }
539     }
540
541     private void addDiscovery(Node node) {
542         Map<Long, ISwitch> switches = controller.getSwitches();
543         ISwitch sw = switches.get((Long) node.getID());
544         List<OFPhysicalPort> ports = sw.getEnabledPorts();
545         if (ports == null) {
546             return;
547         }
548         for (OFPhysicalPort port : ports) {
549             NodeConnector nodeConnector = NodeConnectorCreator
550                     .createOFNodeConnector(port.getPortNumber(), node);
551             if (!readyListHi.contains(nodeConnector)) {
552                 readyListHi.add(nodeConnector);
553             }
554         }
555     }
556
557     private void addDiscovery(NodeConnector nodeConnector) {
558         if (isTracked(nodeConnector)) {
559             return;
560         }
561
562         readyListHi.add(nodeConnector);
563     }
564
565     private Set<NodeConnector> getRemoveSet(Collection<NodeConnector> c,
566             Node node) {
567         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
568         if (c == null) {
569             return removeSet;
570         }
571         for (NodeConnector nodeConnector : c) {
572             if (node.equals(nodeConnector.getNode())) {
573                 removeSet.add(nodeConnector);
574             }
575         }
576         return removeSet;
577     }
578
579     private void removeDiscovery(Node node) {
580         Set<NodeConnector> removeSet;
581
582         removeSet = getRemoveSet(readyListHi, node);
583         readyListHi.removeAll(removeSet);
584
585         removeSet = getRemoveSet(readyListLo, node);
586         readyListLo.removeAll(removeSet);
587
588         removeSet = getRemoveSet(waitingList, node);
589         waitingList.removeAll(removeSet);
590
591         removeSet = getRemoveSet(pendingMap.keySet(), node);
592         for (NodeConnector nodeConnector : removeSet) {
593             pendingMap.remove(nodeConnector);
594         }
595
596         removeSet = getRemoveSet(edgeMap.keySet(), node);
597         for (NodeConnector nodeConnector : removeSet) {
598             removeEdge(nodeConnector, false);
599         }
600
601         removeSet = getRemoveSet(prodMap.keySet(), node);
602         for (NodeConnector nodeConnector : removeSet) {
603             removeProdEdge(nodeConnector);
604         }
605     }
606
607     private void removeDiscovery(NodeConnector nodeConnector) {
608         readyListHi.remove(nodeConnector);
609         readyListLo.remove(nodeConnector);
610         waitingList.remove(nodeConnector);
611         pendingMap.remove(nodeConnector);
612         removeEdge(nodeConnector, false);
613         removeProdEdge(nodeConnector);
614     }
615
616     private void checkTimeout() {
617         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
618         Set<NodeConnector> retrySet = new HashSet<NodeConnector>();
619         int sentCount;
620
621         Set<NodeConnector> pendingSet = pendingMap.keySet();
622         if (pendingSet != null) {
623             for (NodeConnector nodeConnector : pendingSet) {
624                 sentCount = pendingMap.get(nodeConnector);
625                 pendingMap.put(nodeConnector, ++sentCount);
626                 if (sentCount > getDiscoveryFinalTimeoutInterval()) {
627                     // timeout the edge
628                     removeSet.add(nodeConnector);
629                     logger.trace("Discovery timeout {}", nodeConnector);
630                 } else if (sentCount % discoveryTimeoutTicks == 0) {
631                     retrySet.add(nodeConnector);
632                 }
633             }
634         }
635
636         for (NodeConnector nodeConnector : removeSet) {
637             removeEdge(nodeConnector);
638         }
639
640         for (NodeConnector nodeConnector : retrySet) {
641             transmitQ.add(nodeConnector);
642         }
643     }
644
645     private void checkAging() {
646         if (!discoveryAgingEnabled) {
647             return;
648         }
649
650         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
651         int sentCount;
652
653         Set<NodeConnector> agingSet = agingMap.keySet();
654         if (agingSet != null) {
655             for (NodeConnector nodeConnector : agingSet) {
656                 sentCount = agingMap.get(nodeConnector);
657                 agingMap.put(nodeConnector, ++sentCount);
658                 if (sentCount > discoveryAgeoutTicks) {
659                     // age out the edge
660                     removeSet.add(nodeConnector);
661                     logger.trace("Discovery age out {}", nodeConnector);
662                 }
663             }
664         }
665
666         for (NodeConnector nodeConnector : removeSet) {
667             removeProdEdge(nodeConnector);
668         }
669     }
670
671     private void doDiscovery() {
672         if (++discoveryTimerTickCount <= discoveryBatchPauseTicks) {
673             for (NodeConnector nodeConnector : getWorkingSet()) {
674                 pendingMap.put(nodeConnector, 0);
675                 transmitQ.add(nodeConnector);
676             }
677         } else if (discoveryTimerTickCount >= discoveryBatchRestartTicks) {
678             discoveryTimerTickCount = 0;
679             for (NodeConnector nodeConnector : waitingList) {
680                 if (!readyListLo.contains(nodeConnector))
681                     readyListLo.add(nodeConnector);
682             }
683             waitingList.removeAll(readyListLo);
684         }
685     }
686
687     private void doConsistencyCheck() {
688         if (!discoveryConsistencyCheckEnabled) {
689             return;
690         }
691
692         if (++discoveryConsistencyCheckTickCount
693                 % getDiscoveryConsistencyCheckInterval() != 0) {
694             return;
695         }
696
697         discoveryConsistencyCheckCallingTimes++;
698
699         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
700         Set<NodeConnector> ncSet = edgeMap.keySet();
701         if (ncSet == null) {
702             return;
703         }
704         for (NodeConnector nodeConnector : ncSet) {
705             if (!isEnabled(nodeConnector)) {
706                 removeSet.add(nodeConnector);
707                 discoveryConsistencyCheckCorrected++;
708                 logger.debug("ConsistencyChecker: remove disabled {}",
709                         nodeConnector);
710                 continue;
711             }
712
713             if (!isTracked(nodeConnector)) {
714                 waitingList.add(nodeConnector);
715                 discoveryConsistencyCheckCorrected++;
716                 logger.debug("ConsistencyChecker: add back untracked {}",
717                         nodeConnector);
718                 continue;
719             }
720         }
721
722         for (NodeConnector nodeConnector : removeSet) {
723             removeEdge(nodeConnector, false);
724         }
725
726         // remove stale entries
727         removeSet.clear();
728         for (NodeConnector nodeConnector : waitingList) {
729             if (!isEnabled(nodeConnector)) {
730                 removeSet.add(nodeConnector);
731                 discoveryConsistencyCheckCorrected++;
732                 logger.debug("ConsistencyChecker: remove disabled {}",
733                         nodeConnector);
734             }
735         }
736         waitingList.removeAll(removeSet);
737
738         // Get a snapshot of all the existing switches
739         Map<Long, ISwitch> switches = this.controller.getSwitches();
740         for (ISwitch sw : switches.values()) {
741             for (OFPhysicalPort port : sw.getEnabledPorts()) {
742                 Node node = NodeCreator.createOFNode(sw.getId());
743                 NodeConnector nodeConnector = NodeConnectorCreator
744                         .createOFNodeConnector(port.getPortNumber(), node);
745                 if (!isTracked(nodeConnector)) {
746                     waitingList.add(nodeConnector);
747                     discoveryConsistencyCheckCorrected++;
748                     logger.debug("ConsistencyChecker: add back untracked {}",
749                             nodeConnector);
750                 }
751             }
752         }
753     }
754
755     private void addEdge(Edge edge, Set<Property> props) {
756         if (edge == null) {
757             return;
758         }
759
760         NodeConnector src = edge.getTailNodeConnector();
761         if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
762             pendingMap.remove(src);
763             if (!waitingList.contains(src)) {
764                 waitingList.add(src);
765             }
766         } else {
767             NodeConnector dst = edge.getHeadNodeConnector();
768             agingMap.put(dst, 0);
769         }
770
771         // notify routeEngine
772         updateEdge(edge, UpdateType.ADDED, props);
773         logger.trace("Add edge {}", edge);
774     }
775
776     /**
777      * Update Production Edge
778      * 
779      * @param edge
780      *            The Production Edge
781      * @param props
782      *            Properties associated with the edge
783      */
784     private void updateProdEdge(Edge edge, Set<Property> props) {
785         NodeConnector edgePort = edge.getHeadNodeConnector();
786
787         /* Do not update in case there is an existing OpenFlow link */
788         if (edgeMap.get(edgePort) != null) {
789             logger.trace(
790                     "Discarded edge {} since there is an existing OF link {}",
791                     edge, edgeMap.get(edgePort));
792             return;
793         }
794
795         /* Look for any existing Production Edge */
796         Edge oldEdge = prodMap.get(edgePort);
797         if (oldEdge == null) {
798             /* Let's add a new one */
799             addEdge(edge, props);
800         } else if (!edge.equals(oldEdge)) {
801             /* Remove the old one first */
802             removeProdEdge(oldEdge.getHeadNodeConnector());
803             /* Then add the new one */
804             addEdge(edge, props);
805         } else {
806             /* o/w, just reset the aging timer */
807             NodeConnector dst = edge.getHeadNodeConnector();
808             agingMap.put(dst, 0);
809         }
810     }
811
812     /**
813      * Remove Production Edge for a given edge port
814      * 
815      * @param edgePort
816      *            The OF edge port
817      */
818     private void removeProdEdge(NodeConnector edgePort) {
819         agingMap.remove(edgePort);
820
821         Edge edge = null;
822         Set<NodeConnector> prodKeySet = prodMap.keySet();
823         if ((prodKeySet != null) && (prodKeySet.contains(edgePort))) {
824             edge = prodMap.get(edgePort);
825             prodMap.remove(edgePort);
826         }
827
828         // notify Topology
829         if (this.discoveryService != null) {
830             this.discoveryService.notifyEdge(edge, UpdateType.REMOVED, null);
831         }
832         logger.trace("Remove edge {}", edge);
833     }
834
835     /*
836      * Remove OpenFlow edge
837      */
838     private void removeEdge(NodeConnector nodeConnector, boolean stillEnabled) {
839         pendingMap.remove(nodeConnector);
840         readyListLo.remove(nodeConnector);
841         readyListHi.remove(nodeConnector);
842
843         if (stillEnabled) {
844             // keep discovering
845             if (!waitingList.contains(nodeConnector)) {
846                 waitingList.add(nodeConnector);
847             }
848         } else {
849             // stop it
850             waitingList.remove(nodeConnector);
851         }
852
853         Edge edge = null;
854         Set<NodeConnector> edgeKeySet = edgeMap.keySet();
855         if ((edgeKeySet != null) && (edgeKeySet.contains(nodeConnector))) {
856             edge = edgeMap.get(nodeConnector);
857             edgeMap.remove(nodeConnector);
858         }
859
860         // notify Topology
861         if (this.discoveryService != null) {
862             this.discoveryService.notifyEdge(edge, UpdateType.REMOVED, null);
863         }
864         logger.trace("Remove {}", nodeConnector);
865     }
866
867     private void removeEdge(NodeConnector nodeConnector) {
868         removeEdge(nodeConnector, isEnabled(nodeConnector));
869     }
870
871     private void updateEdge(Edge edge, UpdateType type, Set<Property> props) {
872         if (discoveryService == null) {
873             return;
874         }
875
876         this.discoveryService.notifyEdge(edge, type, props);
877
878         NodeConnector src = edge.getTailNodeConnector(), dst = edge
879                 .getHeadNodeConnector();
880         if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
881             if (type == UpdateType.ADDED) {
882                 edgeMap.put(dst, edge);
883             } else {
884                 edgeMap.remove(dst);
885             }
886         } else {
887             /*
888              * Save Production edge into different DB keyed by the Edge port
889              */
890             if (type == UpdateType.ADDED) {
891                 prodMap.put(dst, edge);
892             } else {
893                 prodMap.remove(dst);
894             }
895         }
896     }
897
898     private void moreToReadyListHi(NodeConnector nodeConnector) {
899         if (readyListLo.contains(nodeConnector)) {
900             readyListLo.remove(nodeConnector);
901             readyListHi.add(nodeConnector);
902         } else if (waitingList.contains(nodeConnector)) {
903             waitingList.remove(nodeConnector);
904             readyListHi.add(nodeConnector);
905         }
906     }
907
908     private void registerWithOSGIConsole() {
909         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
910                 .getBundleContext();
911         bundleContext.registerService(CommandProvider.class.getName(), this,
912                 null);
913     }
914
915     private int getDiscoveryConsistencyCheckInterval() {
916         return discoveryConsistencyCheckMultiple * discoveryBatchRestartTicks;
917     }
918
919     private int getDiscoveryFinalTimeoutInterval() {
920         return (discoveryRetry + 1) * discoveryTimeoutTicks;
921     }
922
923     @Override
924     public String getHelp() {
925         StringBuffer help = new StringBuffer();
926         help.append("---Topology Discovery---\n");
927         help.append("\t prlh                            - Print readyListHi entries\n");
928         help.append("\t prll                            - Print readyListLo entries\n");
929         help.append("\t pwl                             - Print waitingList entries\n");
930         help.append("\t ppl                             - Print pendingList entries\n");
931         help.append("\t ptick                           - Print tick time in msec\n");
932         help.append("\t pcc                             - Print CC info\n");
933         help.append("\t psize                           - Print sizes of all the lists\n");
934         help.append("\t ptm                             - Print timeout info\n");
935         help.append("\t ecc                             - Enable CC\n");
936         help.append("\t dcc                             - Disable CC\n");
937         help.append("\t scc [multiple]                  - Set/show CC multiple and interval\n");
938         help.append("\t sports [ports]                  - Set/show max ports per batch\n");
939         help.append("\t spause [ticks]                  - Set/show pause period\n");
940         help.append("\t sdi [ticks]                     - Set/show discovery interval in ticks\n");
941         help.append("\t stm [ticks]                     - Set/show per timeout ticks\n");
942         help.append("\t sretry [count]                  - Set/show num of retries\n");
943         help.append("\t addsw <swid>                    - Add a switch\n");
944         help.append("\t remsw <swid>                    - Remove a switch\n");
945         help.append("\t page                            - Print aging info\n");
946         help.append("\t sage                            - Set/Show aging time limit\n");
947         help.append("\t eage                            - Enable aging\n");
948         help.append("\t dage                            - Disable aging\n");
949         help.append("\t pthrot                          - Print throttling\n");
950         help.append("\t ethrot                          - Enable throttling\n");
951         help.append("\t dthrot                          - Disable throttling\n");
952         help.append("\t psnp                            - Print LLDP snooping\n");
953         help.append("\t esnp <all|nodeConnector>        - Enable LLDP snooping\n");
954         help.append("\t dsnp <all|nodeConnector>        - Disable LLDP snooping\n");
955         return help.toString();
956     }
957
958     public void _prlh(CommandInterpreter ci) {
959         ci.println("ReadyListHi\n");
960         for (NodeConnector nodeConnector : readyListHi) {
961             if (nodeConnector == null) {
962                 continue;
963             }
964             ci.println(nodeConnector);
965         }
966     }
967
968     public void _prll(CommandInterpreter ci) {
969         ci.println("ReadyListLo\n");
970         for (NodeConnector nodeConnector : readyListLo) {
971             if (nodeConnector == null) {
972                 continue;
973             }
974             ci.println(nodeConnector);
975         }
976     }
977
978     public void _pwl(CommandInterpreter ci) {
979         ci.println("WaitingList\n");
980         for (NodeConnector nodeConnector : waitingList) {
981             if (nodeConnector == null) {
982                 continue;
983             }
984             ci.println(nodeConnector);
985         }
986     }
987
988     public void _ppl(CommandInterpreter ci) {
989         ci.println("PendingList\n");
990         for (NodeConnector nodeConnector : pendingMap.keySet()) {
991             if (nodeConnector == null) {
992                 continue;
993             }
994             ci.println(nodeConnector);
995         }
996     }
997
998     public void _ptick(CommandInterpreter ci) {
999         ci.println("Current timer is " + discoveryTimerTick + " msec per tick");
1000     }
1001
1002     public void _pcc(CommandInterpreter ci) {
1003         if (discoveryConsistencyCheckEnabled) {
1004             ci.println("ConsistencyChecker is currently enabled");
1005         } else {
1006             ci.println("ConsistencyChecker is currently disabled");
1007         }
1008         ci.println("Interval " + getDiscoveryConsistencyCheckInterval());
1009         ci.println("Multiple " + discoveryConsistencyCheckMultiple);
1010         ci.println("Number of times called "
1011                 + discoveryConsistencyCheckCallingTimes);
1012         ci.println("Corrected count " + discoveryConsistencyCheckCorrected);
1013     }
1014
1015     public void _ptm(CommandInterpreter ci) {
1016         ci.println("Final timeout ticks " + getDiscoveryFinalTimeoutInterval());
1017         ci.println("Per timeout ticks " + discoveryTimeoutTicks);
1018         ci.println("Number of retries after initial timeout " + discoveryRetry);
1019     }
1020
1021     public void _psize(CommandInterpreter ci) {
1022         ci.println("readyListLo size " + readyListLo.size() + "\n"
1023                 + "readyListHi size " + readyListHi.size() + "\n"
1024                 + "waitingList size " + waitingList.size() + "\n"
1025                 + "pendingMap size " + pendingMap.size() + "\n"
1026                 + "edgeMap size " + edgeMap.size() + "\n" + "prodMap size "
1027                 + prodMap.size() + "\n" + "agingMap size " + agingMap.size());
1028     }
1029
1030     public void _page(CommandInterpreter ci) {
1031         if (this.discoveryAgingEnabled) {
1032             ci.println("Aging is enabled");
1033         } else {
1034             ci.println("Aging is disabled");
1035         }
1036         ci.println("Current aging time limit " + this.discoveryAgeoutTicks);
1037         ci.println("\n");
1038         ci.println("                           Edge                                 Aging ");
1039         Collection<Edge> prodSet = prodMap.values();
1040         if (prodSet == null) {
1041             return;
1042         }
1043         for (Edge edge : prodSet) {
1044             Integer aging = agingMap.get(edge.getHeadNodeConnector());
1045             if (aging != null) {
1046                 ci.println(edge + "      " + aging);
1047             }
1048         }
1049         ci.println("\n");
1050         ci.println("              NodeConnector                                                 Edge ");
1051         Set<NodeConnector> keySet = prodMap.keySet();
1052         if (keySet == null) {
1053             return;
1054         }
1055         for (NodeConnector nc : keySet) {
1056             ci.println(nc + "      " + prodMap.get(nc));
1057         }
1058         return;
1059     }
1060
1061     public void _sage(CommandInterpreter ci) {
1062         String val = ci.nextArgument();
1063         if (val == null) {
1064             ci.println("Please enter aging time limit. Current value "
1065                     + this.discoveryAgeoutTicks);
1066             return;
1067         }
1068         try {
1069             this.discoveryAgeoutTicks = Integer.parseInt(val);
1070         } catch (Exception e) {
1071             ci.println("Please enter a valid number");
1072         }
1073         return;
1074     }
1075
1076     public void _eage(CommandInterpreter ci) {
1077         this.discoveryAgingEnabled = true;
1078         ci.println("Aging is enabled");
1079         return;
1080     }
1081
1082     public void _dage(CommandInterpreter ci) {
1083         this.discoveryAgingEnabled = false;
1084         ci.println("Aging is disabled");
1085         return;
1086     }
1087
1088     public void _scc(CommandInterpreter ci) {
1089         String val = ci.nextArgument();
1090         if (val == null) {
1091             ci.println("Please enter CC multiple. Current multiple "
1092                     + discoveryConsistencyCheckMultiple + " (interval "
1093                     + getDiscoveryConsistencyCheckInterval()
1094                     + ") calling times "
1095                     + discoveryConsistencyCheckCallingTimes);
1096             return;
1097         }
1098         try {
1099             discoveryConsistencyCheckMultiple = Integer.parseInt(val);
1100         } catch (Exception e) {
1101             ci.println("Please enter a valid number");
1102         }
1103         return;
1104     }
1105
1106     public void _ecc(CommandInterpreter ci) {
1107         this.discoveryConsistencyCheckEnabled = true;
1108         ci.println("ConsistencyChecker is enabled");
1109         return;
1110     }
1111
1112     public void _dcc(CommandInterpreter ci) {
1113         this.discoveryConsistencyCheckEnabled = false;
1114         ci.println("ConsistencyChecker is disabled");
1115         return;
1116     }
1117
1118     public void _psnp(CommandInterpreter ci) {
1119         if (this.discoverySnoopingEnabled) {
1120             ci.println("Discovery snooping is globally enabled");
1121         } else {
1122             ci.println("Discovery snooping is globally disabled");
1123         }
1124
1125         ci.println("\nDiscovery snooping is locally disabled on these ports");
1126         for (NodeConnector nodeConnector : discoverySnoopingDisableList) {
1127             ci.println(nodeConnector);
1128         }
1129         return;
1130     }
1131
1132     public void _esnp(CommandInterpreter ci) {
1133         String val = ci.nextArgument();
1134
1135         if (val == null) {
1136             ci.println("Usage: esnp <all|nodeConnector>");
1137         } else if (val.equalsIgnoreCase("all")) {
1138             this.discoverySnoopingEnabled = true;
1139             ci.println("Discovery snooping is globally enabled");
1140         } else {
1141             NodeConnector nodeConnector = NodeConnector.fromString(val);
1142             if (nodeConnector != null) {
1143                 discoverySnoopingDisableList.remove(nodeConnector);
1144                 ci.println("Discovery snooping is locally enabled on port "
1145                         + nodeConnector);
1146             } else {
1147                 ci.println("Entered invalid NodeConnector " + val);
1148             }
1149         }
1150         return;
1151     }
1152
1153     public void _dsnp(CommandInterpreter ci) {
1154         String val = ci.nextArgument();
1155
1156         if (val == null) {
1157             ci.println("Usage: dsnp <all|nodeConnector>");
1158         } else if (val.equalsIgnoreCase("all")) {
1159             this.discoverySnoopingEnabled = false;
1160             ci.println("Discovery snooping is globally disabled");
1161         } else {
1162             NodeConnector nodeConnector = NodeConnector.fromString(val);
1163             if (nodeConnector != null) {
1164                 discoverySnoopingDisableList.add(nodeConnector);
1165                 ci.println("Discovery snooping is locally disabled on port "
1166                         + nodeConnector);
1167             } else {
1168                 ci.println("Entered invalid NodeConnector " + val);
1169             }
1170         }
1171         return;
1172     }
1173
1174     public void _spause(CommandInterpreter ci) {
1175         String val = ci.nextArgument();
1176         String out = "Please enter pause period less than "
1177                 + discoveryBatchRestartTicks + ". Current pause period is "
1178                 + discoveryBatchPausePeriod + " pause tick is "
1179                 + discoveryBatchPauseTicks + ".";
1180
1181         if (val != null) {
1182             try {
1183                 int pause = Integer.parseInt(val);
1184                 if (pause < discoveryBatchRestartTicks) {
1185                     discoveryBatchPausePeriod = pause;
1186                     discoveryBatchPauseTicks = discoveryBatchRestartTicks
1187                             - discoveryBatchPausePeriod;
1188                     return;
1189                 }
1190             } catch (Exception e) {
1191             }
1192         }
1193
1194         ci.println(out);
1195     }
1196
1197     public void _sdi(CommandInterpreter ci) {
1198         String val = ci.nextArgument();
1199         String out = "Please enter discovery interval greater than "
1200                 + discoveryBatchPausePeriod + ". Current value is "
1201                 + discoveryBatchRestartTicks + ".";
1202
1203         if (val != null) {
1204             try {
1205                 int restart = Integer.parseInt(val);
1206                 if (restart > discoveryBatchPausePeriod) {
1207                     discoveryBatchRestartTicks = restart;
1208                     discoveryBatchPauseTicks = discoveryBatchRestartTicks
1209                             - discoveryBatchPausePeriod;
1210                     return;
1211                 }
1212             } catch (Exception e) {
1213             }
1214         }
1215         ci.println(out);
1216     }
1217
1218     public void _sports(CommandInterpreter ci) {
1219         String val = ci.nextArgument();
1220         if (val == null) {
1221             ci.println("Please enter max ports per batch. Current value is "
1222                     + discoveryBatchMaxPorts);
1223             return;
1224         }
1225         try {
1226             discoveryBatchMaxPorts = Integer.parseInt(val);
1227         } catch (Exception e) {
1228             ci.println("Please enter a valid number");
1229         }
1230         return;
1231     }
1232
1233     public void _sretry(CommandInterpreter ci) {
1234         String val = ci.nextArgument();
1235         if (val == null) {
1236             ci.println("Please enter number of retries. Current value is "
1237                     + discoveryRetry);
1238             return;
1239         }
1240         try {
1241             discoveryRetry = Integer.parseInt(val);
1242         } catch (Exception e) {
1243             ci.println("Please enter a valid number");
1244         }
1245         return;
1246     }
1247
1248     public void _stm(CommandInterpreter ci) {
1249         String val = ci.nextArgument();
1250         String out = "Please enter timeout tick value less than "
1251                 + discoveryBatchRestartTicks + ". Current value is "
1252                 + discoveryTimeoutTicks;
1253         if (val != null) {
1254             try {
1255                 int timeout = Integer.parseInt(val);
1256                 if (timeout < discoveryBatchRestartTicks) {
1257                     discoveryTimeoutTicks = timeout;
1258                     return;
1259                 }
1260             } catch (Exception e) {
1261             }
1262         }
1263
1264         ci.println(out);
1265     }
1266
1267     public void _addsw(CommandInterpreter ci) {
1268         String val = ci.nextArgument();
1269         Long sid;
1270         try {
1271             sid = Long.parseLong(val);
1272             Node node = NodeCreator.createOFNode(sid);
1273             addDiscovery(node);
1274         } catch (Exception e) {
1275             ci.println("Please enter a valid number");
1276         }
1277         return;
1278     }
1279
1280     public void _remsw(CommandInterpreter ci) {
1281         String val = ci.nextArgument();
1282         Long sid;
1283         try {
1284             sid = Long.parseLong(val);
1285             Node node = NodeCreator.createOFNode(sid);
1286             removeDiscovery(node);
1287         } catch (Exception e) {
1288             ci.println("Please enter a valid number");
1289         }
1290         return;
1291     }
1292
1293     public void _pthrot(CommandInterpreter ci) {
1294         if (this.throttling) {
1295             ci.println("Throttling is enabled");
1296         } else {
1297             ci.println("Throttling is disabled");
1298         }
1299     }
1300
1301     public void _ethrot(CommandInterpreter ci) {
1302         this.throttling = true;
1303         ci.println("Throttling is enabled");
1304         return;
1305     }
1306
1307     public void _dthrot(CommandInterpreter ci) {
1308         this.throttling = false;
1309         ci.println("Throttling is disabled");
1310         return;
1311     }
1312
1313     @Override
1314     public void updateNode(Node node, UpdateType type, Set<Property> props) {
1315         switch (type) {
1316         case ADDED:
1317             addNode(node, props);
1318             break;
1319         case REMOVED:
1320             removeNode(node);
1321             break;
1322         default:
1323             break;
1324         }
1325     }
1326
1327     @Override
1328     public void updateNodeConnector(NodeConnector nodeConnector,
1329             UpdateType type, Set<Property> props) {
1330         Config config = null;
1331         State state = null;
1332         boolean enabled = false;
1333
1334         for (Property prop : props) {
1335             if (prop.getName().equals(Config.ConfigPropName)) {
1336                 config = (Config) prop;
1337             } else if (prop.getName().equals(State.StatePropName)) {
1338                 state = (State) prop;
1339             }
1340         }
1341         enabled = ((config != null) && (config.getValue() == Config.ADMIN_UP)
1342                 && (state != null) && (state.getValue() == State.EDGE_UP));
1343
1344         switch (type) {
1345         case ADDED:
1346             if (enabled) {
1347                 addDiscovery(nodeConnector);
1348                 logger.trace("ADDED enabled {}", nodeConnector);
1349             } else {
1350                 logger.trace("ADDED disabled {}", nodeConnector);
1351             }
1352             break;
1353         case CHANGED:
1354             if (enabled) {
1355                 addDiscovery(nodeConnector);
1356                 logger.trace("CHANGED enabled {}", nodeConnector);
1357             } else {
1358                 removeDiscovery(nodeConnector);
1359                 logger.trace("CHANGED disabled {}", nodeConnector);
1360             }
1361             break;
1362         case REMOVED:
1363             removeDiscovery(nodeConnector);
1364             logger.trace("REMOVED enabled {}", nodeConnector);
1365             break;
1366         default:
1367             return;
1368         }
1369     }
1370
1371     public void addNode(Node node, Set<Property> props) {
1372         if (node == null)
1373             return;
1374
1375         addDiscovery(node);
1376     }
1377
1378     public void removeNode(Node node) {
1379         if (node == null)
1380             return;
1381
1382         removeDiscovery(node);
1383     }
1384
1385     public void updateNode(Node node, Set<Property> props) {
1386     }
1387
1388     void setController(IController s) {
1389         this.controller = s;
1390     }
1391
1392     void unsetController(IController s) {
1393         if (this.controller == s) {
1394             this.controller = null;
1395         }
1396     }
1397
1398     public void setPluginInInventoryService(IPluginInInventoryService service) {
1399         this.pluginInInventoryService = service;
1400     }
1401
1402     public void unsetPluginInInventoryService(IPluginInInventoryService service) {
1403         this.pluginInInventoryService = null;
1404     }
1405
1406     public void setIDataPacketMux(IDataPacketMux service) {
1407         this.iDataPacketMux = service;
1408     }
1409
1410     public void unsetIDataPacketMux(IDataPacketMux service) {
1411         if (this.iDataPacketMux == service) {
1412             this.iDataPacketMux = null;
1413         }
1414     }
1415
1416     void setDiscoveryService(IDiscoveryService s) {
1417         this.discoveryService = s;
1418     }
1419
1420     void unsetDiscoveryService(IDiscoveryService s) {
1421         if (this.discoveryService == s) {
1422             this.discoveryService = null;
1423         }
1424     }
1425
1426     private void initDiscoveryPacket() {
1427         // Create LLDP ChassisID TLV
1428         chassisIdTlv = new LLDPTLV();
1429         chassisIdTlv.setType((byte) LLDPTLV.TLVType.ChassisID.getValue());
1430
1431         // Create LLDP PortID TLV
1432         portIdTlv = new LLDPTLV();
1433         portIdTlv.setType((byte) LLDPTLV.TLVType.PortID.getValue());
1434
1435         // Create LLDP TTL TLV
1436         byte[] ttl = new byte[] { (byte) 0, (byte) 120 };
1437         ttlTlv = new LLDPTLV();
1438         ttlTlv.setType((byte) LLDPTLV.TLVType.TTL.getValue())
1439                 .setLength((short) ttl.length).setValue(ttl);
1440
1441         customTlv = new LLDPTLV();
1442     }
1443
1444     /**
1445      * Function called by the dependency manager when all the required
1446      * dependencies are satisfied
1447      * 
1448      */
1449     void init() {
1450         logger.trace("Init called");
1451
1452         transmitQ = new LinkedBlockingQueue<NodeConnector>();
1453
1454         readyListHi = new CopyOnWriteArrayList<NodeConnector>();
1455         readyListLo = new CopyOnWriteArrayList<NodeConnector>();
1456         waitingList = new CopyOnWriteArrayList<NodeConnector>();
1457         pendingMap = new ConcurrentHashMap<NodeConnector, Integer>();
1458         edgeMap = new ConcurrentHashMap<NodeConnector, Edge>();
1459         agingMap = new ConcurrentHashMap<NodeConnector, Integer>();
1460         prodMap = new ConcurrentHashMap<NodeConnector, Edge>();
1461         discoverySnoopingDisableList = new CopyOnWriteArrayList<NodeConnector>();
1462
1463         discoveryTimer = new Timer("DiscoveryService");
1464         discoveryTimerTask = new DiscoveryTimerTask();
1465
1466         transmitThread = new Thread(new DiscoveryTransmit(transmitQ));
1467
1468         initDiscoveryPacket();
1469
1470         registerWithOSGIConsole();
1471     }
1472
1473     /**
1474      * Function called by the dependency manager when at least one dependency
1475      * become unsatisfied or when the component is shutting down because for
1476      * example bundle is being stopped.
1477      * 
1478      */
1479     void destroy() {
1480         transmitQ = null;
1481         readyListHi = null;
1482         readyListLo = null;
1483         waitingList = null;
1484         pendingMap = null;
1485         edgeMap = null;
1486         agingMap = null;
1487         prodMap = null;
1488         discoveryTimer = null;
1489         discoveryTimerTask = null;
1490         transmitThread = null;
1491     }
1492
1493     /**
1494      * Function called by dependency manager after "init ()" is called and after
1495      * the services provided by the class are registered in the service registry
1496      * 
1497      */
1498     void start() {
1499         discoveryTimer.schedule(discoveryTimerTask, discoveryTimerTick,
1500                 discoveryTimerTick);
1501         transmitThread.start();
1502     }
1503
1504     /**
1505      * Function called after registering the service in OSGi service registry.
1506      */
1507     void started() {
1508         /* get a snapshot of all the existing switches */
1509         addDiscovery();
1510     }
1511
1512     /**
1513      * Function called by the dependency manager before the services exported by
1514      * the component are unregistered, this will be followed by a "destroy ()"
1515      * calls
1516      * 
1517      */
1518     void stop() {
1519         shuttingDown = true;
1520         discoveryTimer.cancel();
1521         transmitThread.interrupt();
1522     }
1523
1524     @Override
1525     public void tagUpdated(String containerName, Node n, short oldTag,
1526             short newTag, UpdateType t) {
1527     }
1528
1529     @Override
1530     public void containerFlowUpdated(String containerName,
1531             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
1532     }
1533
1534     @Override
1535     public void nodeConnectorUpdated(String containerName, NodeConnector p,
1536             UpdateType t) {
1537         switch (t) {
1538         case ADDED:
1539             moreToReadyListHi(p);
1540             break;
1541         default:
1542             break;
1543         }
1544     }
1545
1546     @Override
1547     public void containerModeUpdated(UpdateType t) {
1548         // do nothing
1549     }
1550
1551     private byte[] getSouceMACFromNodeID(String nodeId) {
1552         byte[] cid = HexEncode.bytesFromHexString(nodeId);
1553         byte[] sourceMac = new byte[6];
1554         int pos = cid.length - sourceMac.length;
1555
1556         if (pos >= 0) {
1557             System.arraycopy(cid, pos, sourceMac, 0, sourceMac.length);
1558         }
1559
1560         return sourceMac;
1561     }
1562
1563     /**
1564      * This method returns the interval which determines how often the discovery
1565      * packets will be sent. Default is 300 seconds.
1566      * 
1567      * @return The discovery interval in second
1568      */
1569     private int getDiscoveryInterval() {
1570         String elapsedTime = System.getProperty("of.discoveryInterval");
1571         int rv = 300;
1572
1573         try {
1574             if (elapsedTime != null) {
1575                 rv = Integer.parseInt(elapsedTime);
1576             }
1577         } catch (Exception e) {
1578         }
1579
1580         return rv;
1581     }
1582
1583     /**
1584      * This method returns the timeout value in waiting for response of a
1585      * discovery query. Default is 60 seconds.
1586      * 
1587      * @return The discovery timeout in second
1588      */
1589     private int getDiscoveryTimeout() {
1590         String elapsedTime = System.getProperty("of.discoveryTimeout");
1591         int rv = 60;
1592
1593         try {
1594             if (elapsedTime != null) {
1595                 rv = Integer.parseInt(elapsedTime);
1596             }
1597         } catch (Exception e) {
1598         }
1599
1600         return rv;
1601     }
1602
1603     /**
1604      * This method returns the number of retries after the initial discovery
1605      * packet is not received within the timeout period. Default is 2 times.
1606      * 
1607      * @return The number of discovery retries
1608      */
1609     private int getDiscoveryRetry() {
1610         String retry = System.getProperty("of.discoveryRetry");
1611         int rv = 2;
1612
1613         if (retry != null) {
1614             try {
1615                 rv = Integer.parseInt(retry);
1616             } catch (Exception e) {
1617             }
1618         }
1619
1620         return rv;
1621     }
1622 }