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