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