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