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