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