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