Fix NPEs on switch disconnect in cluster mode
[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         if (sw == null) {
564             //switch could be removed by now, stop propagation
565             return;
566         }
567         List<OFPhysicalPort> ports = sw.getEnabledPorts();
568         for (OFPhysicalPort port : ports) {
569             NodeConnector nodeConnector = NodeConnectorCreator.createOFNodeConnector(port.getPortNumber(), node);
570             if (!readyListHi.contains(nodeConnector)) {
571                 readyListHi.add(nodeConnector);
572             }
573         }
574     }
575
576     private void addDiscovery(NodeConnector nodeConnector) {
577         if (isTracked(nodeConnector)) {
578             return;
579         }
580
581         readyListHi.add(nodeConnector);
582     }
583
584     private void removeNodeConnector(NodeConnector nodeConnector) {
585         readyListLo.remove(nodeConnector);
586         readyListHi.remove(nodeConnector);
587         stagingList.remove(nodeConnector);
588         holdTime.remove(nodeConnector);
589         elapsedTime.remove(nodeConnector);
590     }
591
592     private Set<NodeConnector> getRemoveSet(Collection<NodeConnector> c, Node node) {
593         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
594         if (c == null) {
595             return removeSet;
596         }
597         for (NodeConnector nodeConnector : c) {
598             if (node.equals(nodeConnector.getNode())) {
599                 removeSet.add(nodeConnector);
600             }
601         }
602         return removeSet;
603     }
604
605     private void removeDiscovery(Node node) {
606         Set<NodeConnector> removeSet;
607
608         removeSet = getRemoveSet(edgeMap.keySet(), node);
609         NodeConnector peerConnector;
610         Edge edge1, edge2;
611         for (NodeConnector nodeConnector : removeSet) {
612             // get the peer for fast removal of the edge in reverse direction
613             peerConnector = null;
614             edge1 = edgeMap.get(nodeConnector);
615             if (edge1 != null) {
616                 edge2 = edgeMap.get(edge1.getTailNodeConnector());
617                 if ((edge2 != null) && node.equals(edge2.getTailNodeConnector().getNode())) {
618                     peerConnector = edge2.getHeadNodeConnector();
619                 }
620             }
621
622             removeEdge(nodeConnector, false);
623             removeEdge(peerConnector, isEnabled(peerConnector));
624         }
625
626         removeSet = getRemoveSet(prodMap.keySet(), node);
627         for (NodeConnector nodeConnector : removeSet) {
628             removeProdEdge(nodeConnector);
629         }
630
631         removeSet = getRemoveSet(readyListHi, node);
632         readyListHi.removeAll(removeSet);
633
634         removeSet = getRemoveSet(readyListLo, node);
635         readyListLo.removeAll(removeSet);
636
637         removeSet = getRemoveSet(stagingList, node);
638         stagingList.removeAll(removeSet);
639
640         removeSet = getRemoveSet(holdTime.keySet(), node);
641         for (NodeConnector nodeConnector : removeSet) {
642             holdTime.remove(nodeConnector);
643         }
644
645         removeSet = getRemoveSet(elapsedTime.keySet(), node);
646         for (NodeConnector nodeConnector : removeSet) {
647             elapsedTime.remove(nodeConnector);
648         }
649     }
650
651     private void removeDiscovery(NodeConnector nodeConnector) {
652         removeNodeConnector(nodeConnector);
653         removeEdge(nodeConnector, false);
654         removeProdEdge(nodeConnector);
655     }
656
657     private void checkTimeout() {
658         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
659         int ticks;
660
661         Set<NodeConnector> monitorSet = holdTime.keySet();
662         if (monitorSet != null) {
663             for (NodeConnector nodeConnector : monitorSet) {
664                 ticks = holdTime.get(nodeConnector);
665                 holdTime.put(nodeConnector, ++ticks);
666                 if (ticks >= discoveryTimeoutTicks) {
667                     // timeout the edge
668                     removeSet.add(nodeConnector);
669                     logger.trace("Discovery timeout {}", nodeConnector);
670                 }
671             }
672         }
673
674         for (NodeConnector nodeConnector : removeSet) {
675             removeEdge(nodeConnector);
676         }
677
678         Set<NodeConnector> retrySet = new HashSet<NodeConnector>();
679         Set<NodeConnector> ncSet = elapsedTime.keySet();
680         if ((ncSet != null) && (ncSet.size() > 0)) {
681             for (NodeConnector nodeConnector : ncSet) {
682                 ticks = elapsedTime.get(nodeConnector);
683                 elapsedTime.put(nodeConnector, ++ticks);
684                 if (ticks >= discoveryThresholdTicks) {
685                     retrySet.add(nodeConnector);
686                 }
687             }
688
689             for (NodeConnector nodeConnector : retrySet) {
690                 // Allow one more retry
691                 elapsedTime.remove(nodeConnector);
692                 if (connectionOutService.isLocal(nodeConnector.getNode())) {
693                     transmitQ.add(nodeConnector);
694                 }
695             }
696         }
697     }
698
699     private void checkAging() {
700         if (!discoveryAgingEnabled) {
701             return;
702         }
703
704         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
705         int ticks;
706
707         Set<NodeConnector> agingSet = agingMap.keySet();
708         if (agingSet != null) {
709             for (NodeConnector nodeConnector : agingSet) {
710                 ticks = agingMap.get(nodeConnector);
711                 agingMap.put(nodeConnector, ++ticks);
712                 if (ticks > discoveryAgeoutTicks) {
713                     // age out the edge
714                     removeSet.add(nodeConnector);
715                     logger.trace("Discovery age out {}", nodeConnector);
716                 }
717             }
718         }
719
720         for (NodeConnector nodeConnector : removeSet) {
721             removeProdEdge(nodeConnector);
722         }
723     }
724
725     private void doDiscovery() {
726         if (++discoveryTimerTickCount <= discoveryBatchPauseTicks) {
727             for (NodeConnector nodeConnector : getWorkingSet()) {
728                 if (connectionOutService.isLocal(nodeConnector.getNode())) {
729                     transmitQ.add(nodeConnector);
730                     // Move to staging area after it's served
731                     if (!stagingList.contains(nodeConnector)) {
732                         stagingList.add(nodeConnector);
733                     }
734                 }
735             }
736         } else if (discoveryTimerTickCount >= discoveryBatchRestartTicks) {
737             discoveryTimerTickCount = 0;
738             for (NodeConnector nodeConnector : stagingList) {
739                 if (!readyListLo.contains(nodeConnector)) {
740                     readyListLo.add(nodeConnector);
741                 }
742             }
743             stagingList.removeAll(readyListLo);
744         }
745     }
746
747     private void doConsistencyCheck() {
748         if (!discoveryConsistencyCheckEnabled) {
749             return;
750         }
751
752         if (++discoveryConsistencyCheckTickCount % getDiscoveryConsistencyCheckInterval() != 0) {
753             return;
754         }
755
756         discoveryConsistencyCheckCallingTimes++;
757
758         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
759         Set<NodeConnector> ncSet = edgeMap.keySet();
760         if (ncSet == null) {
761             return;
762         }
763         for (NodeConnector nodeConnector : ncSet) {
764             if (!isEnabled(nodeConnector)) {
765                 removeSet.add(nodeConnector);
766                 discoveryConsistencyCheckCorrected++;
767                 logger.debug("ConsistencyChecker: remove disabled {}", nodeConnector);
768                 continue;
769             }
770
771             if (!isTracked(nodeConnector)) {
772                 stagingList.add(nodeConnector);
773                 discoveryConsistencyCheckCorrected++;
774                 logger.debug("ConsistencyChecker: add back untracked {}", nodeConnector);
775                 continue;
776             }
777         }
778
779         for (NodeConnector nodeConnector : removeSet) {
780             removeEdge(nodeConnector, false);
781         }
782
783         // remove stale entries
784         removeSet.clear();
785         for (NodeConnector nodeConnector : stagingList) {
786             if (!isEnabled(nodeConnector)) {
787                 removeSet.add(nodeConnector);
788                 discoveryConsistencyCheckCorrected++;
789                 logger.debug("ConsistencyChecker: remove disabled {}", nodeConnector);
790             }
791         }
792         stagingList.removeAll(removeSet);
793
794         // Get a snapshot of all the existing switches
795         Map<Long, ISwitch> switches = this.controller.getSwitches();
796         for (ISwitch sw : switches.values()) {
797             for (OFPhysicalPort port : sw.getEnabledPorts()) {
798                 Node node = NodeCreator.createOFNode(sw.getId());
799                 NodeConnector nodeConnector = NodeConnectorCreator.createOFNodeConnector(port.getPortNumber(), node);
800                 if (!isTracked(nodeConnector)) {
801                     stagingList.add(nodeConnector);
802                     discoveryConsistencyCheckCorrected++;
803                     logger.debug("ConsistencyChecker: add back untracked {}", nodeConnector);
804                 }
805             }
806         }
807     }
808
809     private void addEdge(Edge edge, Set<Property> props) {
810         if (edge == null) {
811             return;
812         }
813
814         NodeConnector src = edge.getTailNodeConnector();
815         NodeConnector dst = edge.getHeadNodeConnector();
816         if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
817             holdTime.put(dst, 0);
818         } else {
819             agingMap.put(dst, 0);
820         }
821         elapsedTime.remove(src);
822
823         // fast discovery of the edge in reverse direction
824         if (!edgeMap.containsKey(dst) && !readyListHi.contains(dst) && !elapsedTime.keySet().contains(dst)) {
825             moveToReadyListHi(dst);
826         }
827
828         // notify
829         updateEdge(edge, UpdateType.ADDED, props);
830         logger.trace("Add edge {}", edge);
831     }
832
833     /**
834      * Update Production Edge
835      *
836      * @param edge
837      *            The Production Edge
838      * @param props
839      *            Properties associated with the edge
840      */
841     private void updateProdEdge(Edge edge, Set<Property> props) {
842         NodeConnector edgePort = edge.getHeadNodeConnector();
843
844         /* Do not update in case there is an existing OpenFlow link */
845         if (edgeMap.get(edgePort) != null) {
846             logger.trace("Discarded edge {} since there is an existing OF link {}", edge, edgeMap.get(edgePort));
847             return;
848         }
849
850         /* Look for any existing Production Edge */
851         Edge oldEdge = prodMap.get(edgePort);
852         if (oldEdge == null) {
853             /* Let's add a new one */
854             addEdge(edge, props);
855         } else if (!edge.equals(oldEdge)) {
856             /* Remove the old one first */
857             removeProdEdge(oldEdge.getHeadNodeConnector());
858             /* Then add the new one */
859             addEdge(edge, props);
860         } else {
861             /* o/w, just reset the aging timer */
862             NodeConnector dst = edge.getHeadNodeConnector();
863             agingMap.put(dst, 0);
864         }
865     }
866
867     /**
868      * Remove Production Edge for a given edge port
869      *
870      * @param edgePort
871      *            The OF edge port
872      */
873     private void removeProdEdge(NodeConnector edgePort) {
874         agingMap.remove(edgePort);
875
876         Edge edge = null;
877         Set<NodeConnector> prodKeySet = prodMap.keySet();
878         if ((prodKeySet != null) && (prodKeySet.contains(edgePort))) {
879             edge = prodMap.get(edgePort);
880             prodMap.remove(edgePort);
881         }
882
883         // notify Topology
884         if (this.discoveryListener != null) {
885             this.discoveryListener.notifyEdge(edge, UpdateType.REMOVED, null);
886         }
887         logger.trace("Remove edge {}", edge);
888     }
889
890     /*
891      * Remove OpenFlow edge
892      */
893     private void removeEdge(NodeConnector nodeConnector, boolean stillEnabled) {
894         if (nodeConnector == null) {
895             return;
896         }
897
898         removeNodeConnector(nodeConnector);
899
900         if (stillEnabled) {
901             // keep discovering
902             stagingList.add(nodeConnector);
903         }
904
905         Edge edge = null;
906         Set<NodeConnector> edgeKeySet = edgeMap.keySet();
907         if ((edgeKeySet != null) && (edgeKeySet.contains(nodeConnector))) {
908             edge = edgeMap.get(nodeConnector);
909             edgeMap.remove(nodeConnector);
910         }
911
912         // notify Topology
913         if (this.discoveryListener != null) {
914             this.discoveryListener.notifyEdge(edge, UpdateType.REMOVED, null);
915         }
916         logger.trace("Remove {}", nodeConnector);
917     }
918
919     private void removeEdge(NodeConnector nodeConnector) {
920         removeEdge(nodeConnector, isEnabled(nodeConnector));
921     }
922
923     private void updateEdge(Edge edge, UpdateType type, Set<Property> props) {
924         if (discoveryListener == null) {
925             return;
926         }
927
928         this.discoveryListener.notifyEdge(edge, type, props);
929
930         NodeConnector src = edge.getTailNodeConnector(), dst = edge.getHeadNodeConnector();
931         if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
932             if (type == UpdateType.ADDED) {
933                 edgeMap.put(dst, edge);
934             } else {
935                 edgeMap.remove(dst);
936             }
937         } else {
938             /*
939              * Save Production edge into different DB keyed by the Edge port
940              */
941             if (type == UpdateType.ADDED) {
942                 prodMap.put(dst, edge);
943             } else {
944                 prodMap.remove(dst);
945             }
946         }
947     }
948
949     private void moveToReadyListHi(NodeConnector nodeConnector) {
950         if (readyListLo.contains(nodeConnector)) {
951             readyListLo.remove(nodeConnector);
952         } else if (stagingList.contains(nodeConnector)) {
953             stagingList.remove(nodeConnector);
954         }
955         readyListHi.add(nodeConnector);
956     }
957
958     private void registerWithOSGIConsole() {
959         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
960         bundleContext.registerService(CommandProvider.class.getName(), this, null);
961     }
962
963     private int getDiscoveryConsistencyCheckInterval() {
964         return discoveryConsistencyCheckMultiple * discoveryBatchRestartTicks;
965     }
966
967     @Override
968     public String getHelp() {
969         StringBuffer help = new StringBuffer();
970         help.append("---Topology Discovery---\n");
971         help.append("\t prlh                            - Print readyListHi entries\n");
972         help.append("\t prll                            - Print readyListLo entries\n");
973         help.append("\t psl                             - Print stagingList entries\n");
974         help.append("\t pht                             - Print hold time\n");
975         help.append("\t pet                             - Print elapsed time\n");
976         help.append("\t ptick                           - Print tick time in msec\n");
977         help.append("\t pcc                             - Print CC info\n");
978         help.append("\t psize                           - Print sizes of all the lists\n");
979         help.append("\t ptm                             - Print timeout info\n");
980         help.append("\t ecc                             - Enable CC\n");
981         help.append("\t dcc                             - Disable CC\n");
982         help.append("\t scc [multiple]                  - Set/show CC multiple and interval\n");
983         help.append("\t sports [ports]                  - Set/show max ports per batch\n");
984         help.append("\t spause [ticks]                  - Set/show pause period\n");
985         help.append("\t sdi [ticks]                     - Set/show discovery interval in ticks\n");
986         help.append("\t addsw <swid>                    - Add a switch\n");
987         help.append("\t remsw <swid>                    - Remove a switch\n");
988         help.append("\t page                            - Print aging info\n");
989         help.append("\t sage                            - Set/Show aging time limit\n");
990         help.append("\t eage                            - Enable aging\n");
991         help.append("\t dage                            - Disable aging\n");
992         help.append("\t pthrot                          - Print throttling\n");
993         help.append("\t ethrot                          - Enable throttling\n");
994         help.append("\t dthrot                          - Disable throttling\n");
995         help.append("\t psnp                            - Print LLDP snooping\n");
996         help.append("\t esnp <all|nodeConnector>        - Enable LLDP snooping\n");
997         help.append("\t dsnp <all|nodeConnector>        - Disable LLDP snooping\n");
998         return help.toString();
999     }
1000
1001     private List<NodeConnector> sortList(Collection<NodeConnector> ncs) {
1002         List<String> ncStrArray = new ArrayList<String>();
1003         for (NodeConnector nc : ncs) {
1004             ncStrArray.add(nc.toString());
1005         }
1006         Collections.sort(ncStrArray);
1007
1008         List<NodeConnector> sortedNodeConnectors = new ArrayList<NodeConnector>();
1009         for (String ncStr : ncStrArray) {
1010             sortedNodeConnectors.add(NodeConnector.fromString(ncStr));
1011         }
1012
1013         return sortedNodeConnectors;
1014     }
1015
1016     public void _prlh(CommandInterpreter ci) {
1017         ci.println("readyListHi\n");
1018         for (NodeConnector nodeConnector : sortList(readyListHi)) {
1019             if (nodeConnector == null) {
1020                 continue;
1021             }
1022             ci.println(nodeConnector);
1023         }
1024         ci.println("Total number of Node Connectors: " + readyListHi.size());
1025     }
1026
1027     public void _prll(CommandInterpreter ci) {
1028         ci.println("readyListLo\n");
1029         for (NodeConnector nodeConnector : sortList(readyListLo)) {
1030             if (nodeConnector == null) {
1031                 continue;
1032             }
1033             ci.println(nodeConnector);
1034         }
1035         ci.println("Total number of Node Connectors: " + readyListLo.size());
1036     }
1037
1038     public void _psl(CommandInterpreter ci) {
1039         ci.println("stagingList\n");
1040         for (NodeConnector nodeConnector : sortList(stagingList)) {
1041             if (nodeConnector == null) {
1042                 continue;
1043             }
1044             ci.println(nodeConnector);
1045         }
1046         ci.println("Total number of Node Connectors: " + stagingList.size());
1047     }
1048
1049     public void _pht(CommandInterpreter ci) {
1050         ci.println("          NodeConnector            Last rx LLDP (sec)");
1051         for (ConcurrentMap.Entry<NodeConnector, Integer> entry: holdTime.entrySet()) {
1052             ci.println(entry.getKey() + "\t\t" + entry.getValue() * (discoveryTimerTick / 1000));
1053         }
1054         ci.println("\nSize: " + holdTime.size() + "\tTimeout: " + discoveryTimeoutTicks * (discoveryTimerTick / 1000)
1055                 + " sec");
1056     }
1057
1058     public void _pet(CommandInterpreter ci) {
1059         ci.println("          NodeConnector            Elapsed Time (sec)");
1060         for (ConcurrentMap.Entry<NodeConnector, Integer> entry: elapsedTime.entrySet()) {
1061             ci.println(entry.getKey() + "\t\t" + entry.getValue() * (discoveryTimerTick / 1000));
1062         }
1063         ci.println("\nSize: " + elapsedTime.size() + "\tThreshold: " + DiscoveryPeriod.THRESHOLD.getTime() + " sec");
1064     }
1065
1066     public void _ptick(CommandInterpreter ci) {
1067         ci.println("Current timer is " + discoveryTimerTick + " msec per tick");
1068     }
1069
1070     public void _pcc(CommandInterpreter ci) {
1071         if (discoveryConsistencyCheckEnabled) {
1072             ci.println("ConsistencyChecker is currently enabled");
1073         } else {
1074             ci.println("ConsistencyChecker is currently disabled");
1075         }
1076         ci.println("Interval " + getDiscoveryConsistencyCheckInterval());
1077         ci.println("Multiple " + discoveryConsistencyCheckMultiple);
1078         ci.println("Number of times called " + discoveryConsistencyCheckCallingTimes);
1079         ci.println("Corrected count " + discoveryConsistencyCheckCorrected);
1080     }
1081
1082     public void _ptm(CommandInterpreter ci) {
1083         ci.println("Timeout " + discoveryTimeoutTicks + " ticks, " + discoveryTimerTick / 1000 + " sec per tick.");
1084     }
1085
1086     public void _psize(CommandInterpreter ci) {
1087         ci.println("readyListLo size " + readyListLo.size() + "\n" + "readyListHi size " + readyListHi.size() + "\n"
1088                 + "stagingList size " + stagingList.size() + "\n" + "holdTime size " + holdTime.size() + "\n"
1089                 + "edgeMap size " + edgeMap.size() + "\n" + "prodMap size " + prodMap.size() + "\n" + "agingMap size "
1090                 + agingMap.size() + "\n" + "elapsedTime size " + elapsedTime.size());
1091     }
1092
1093     public void _page(CommandInterpreter ci) {
1094         if (this.discoveryAgingEnabled) {
1095             ci.println("Aging is enabled");
1096         } else {
1097             ci.println("Aging is disabled");
1098         }
1099         ci.println("Current aging time limit " + this.discoveryAgeoutTicks);
1100         ci.println("\n");
1101         ci.println("                           Edge                                 Aging ");
1102         Collection<Edge> prodSet = prodMap.values();
1103         if (prodSet == null) {
1104             return;
1105         }
1106         for (Edge edge : prodSet) {
1107             Integer aging = agingMap.get(edge.getHeadNodeConnector());
1108             if (aging != null) {
1109                 ci.println(edge + "      " + aging);
1110             }
1111         }
1112         ci.println("\n");
1113         ci.println("              NodeConnector                                                 Edge ");
1114         Set<NodeConnector> keySet = prodMap.keySet();
1115         if (keySet == null) {
1116             return;
1117         }
1118         for (NodeConnector nc : keySet) {
1119             ci.println(nc + "      " + prodMap.get(nc));
1120         }
1121         return;
1122     }
1123
1124     public void _sage(CommandInterpreter ci) {
1125         String val = ci.nextArgument();
1126         if (val == null) {
1127             ci.println("Please enter aging time limit. Current value " + this.discoveryAgeoutTicks);
1128             return;
1129         }
1130         try {
1131             this.discoveryAgeoutTicks = Integer.parseInt(val);
1132         } catch (Exception e) {
1133             ci.println("Please enter a valid number");
1134         }
1135         return;
1136     }
1137
1138     public void _eage(CommandInterpreter ci) {
1139         this.discoveryAgingEnabled = true;
1140         ci.println("Aging is enabled");
1141         return;
1142     }
1143
1144     public void _dage(CommandInterpreter ci) {
1145         this.discoveryAgingEnabled = false;
1146         ci.println("Aging is disabled");
1147         return;
1148     }
1149
1150     public void _scc(CommandInterpreter ci) {
1151         String val = ci.nextArgument();
1152         if (val == null) {
1153             ci.println("Please enter CC multiple. Current multiple " + discoveryConsistencyCheckMultiple
1154                     + " (interval " + getDiscoveryConsistencyCheckInterval() + ") calling times "
1155                     + discoveryConsistencyCheckCallingTimes);
1156             return;
1157         }
1158         try {
1159             discoveryConsistencyCheckMultiple = Integer.parseInt(val);
1160         } catch (Exception e) {
1161             ci.println("Please enter a valid number");
1162         }
1163         return;
1164     }
1165
1166     public void _ecc(CommandInterpreter ci) {
1167         this.discoveryConsistencyCheckEnabled = true;
1168         ci.println("ConsistencyChecker is enabled");
1169         return;
1170     }
1171
1172     public void _dcc(CommandInterpreter ci) {
1173         this.discoveryConsistencyCheckEnabled = false;
1174         ci.println("ConsistencyChecker is disabled");
1175         return;
1176     }
1177
1178     public void _psnp(CommandInterpreter ci) {
1179         if (this.discoverySnoopingEnabled) {
1180             ci.println("Discovery snooping is globally enabled");
1181         } else {
1182             ci.println("Discovery snooping is globally disabled");
1183         }
1184
1185         ci.println("\nDiscovery snooping is locally disabled on these ports");
1186         for (NodeConnector nodeConnector : discoverySnoopingDisableList) {
1187             ci.println(nodeConnector);
1188         }
1189         return;
1190     }
1191
1192     public void _esnp(CommandInterpreter ci) {
1193         String val = ci.nextArgument();
1194
1195         if (val == null) {
1196             ci.println("Usage: esnp <all|nodeConnector>");
1197         } else if (val.equalsIgnoreCase("all")) {
1198             this.discoverySnoopingEnabled = true;
1199             ci.println("Discovery snooping is globally enabled");
1200         } else {
1201             NodeConnector nodeConnector = NodeConnector.fromString(val);
1202             if (nodeConnector != null) {
1203                 discoverySnoopingDisableList.remove(nodeConnector);
1204                 ci.println("Discovery snooping is locally enabled on port " + nodeConnector);
1205             } else {
1206                 ci.println("Entered invalid NodeConnector " + val);
1207             }
1208         }
1209         return;
1210     }
1211
1212     public void _dsnp(CommandInterpreter ci) {
1213         String val = ci.nextArgument();
1214
1215         if (val == null) {
1216             ci.println("Usage: dsnp <all|nodeConnector>");
1217         } else if (val.equalsIgnoreCase("all")) {
1218             this.discoverySnoopingEnabled = false;
1219             ci.println("Discovery snooping is globally disabled");
1220         } else {
1221             NodeConnector nodeConnector = NodeConnector.fromString(val);
1222             if (nodeConnector != null) {
1223                 discoverySnoopingDisableList.add(nodeConnector);
1224                 ci.println("Discovery snooping is locally disabled on port " + nodeConnector);
1225             } else {
1226                 ci.println("Entered invalid NodeConnector " + val);
1227             }
1228         }
1229         return;
1230     }
1231
1232     public void _spause(CommandInterpreter ci) {
1233         String val = ci.nextArgument();
1234         String out = "Please enter pause period less than " + discoveryBatchRestartTicks + ". Current pause period is "
1235                 + discoveryBatchPausePeriod + " ticks, pause at " + discoveryBatchPauseTicks + " ticks, "
1236                 + discoveryTimerTick / 1000 + " sec per tick.";
1237
1238         if (val != null) {
1239             try {
1240                 int pause = Integer.parseInt(val);
1241                 if (pause < discoveryBatchRestartTicks) {
1242                     discoveryBatchPausePeriod = pause;
1243                     discoveryBatchPauseTicks = getDiscoveryPauseInterval();
1244                     return;
1245                 }
1246             } catch (Exception e) {
1247             }
1248         }
1249
1250         ci.println(out);
1251     }
1252
1253     public void _sdi(CommandInterpreter ci) {
1254         String val = ci.nextArgument();
1255         String out = "Please enter discovery interval in ticks. Current value is " + discoveryBatchRestartTicks + " ticks, "
1256                 + discoveryTimerTick / 1000 + " sec per tick.";
1257
1258         if (val != null) {
1259             try {
1260                 int ticks;
1261                 Set<NodeConnector> monitorSet = holdTime.keySet();
1262                 if (monitorSet != null) {
1263                     for (NodeConnector nodeConnector : monitorSet) {
1264                         holdTime.put(nodeConnector, 0);
1265                     }
1266                 }
1267
1268                 ticks = Integer.parseInt(val);
1269                 DiscoveryPeriod.INTERVAL.setTick(ticks);
1270                 discoveryBatchRestartTicks = getDiscoveryInterval();
1271                 discoveryBatchPauseTicks = getDiscoveryPauseInterval();
1272                 discoveryTimeoutTicks = getDiscoveryTimeout();
1273                 return;
1274             } catch (Exception e) {
1275             }
1276         }
1277         ci.println(out);
1278     }
1279
1280     public void _sports(CommandInterpreter ci) {
1281         String val = ci.nextArgument();
1282         if (val == null) {
1283             ci.println("Please enter max ports per batch. Current value is " + discoveryBatchMaxPorts);
1284             return;
1285         }
1286         try {
1287             discoveryBatchMaxPorts = Integer.parseInt(val);
1288         } catch (Exception e) {
1289             ci.println("Please enter a valid number");
1290         }
1291         return;
1292     }
1293
1294     public void _addsw(CommandInterpreter ci) {
1295         String val = ci.nextArgument();
1296         Long sid;
1297         try {
1298             sid = Long.parseLong(val);
1299             Node node = NodeCreator.createOFNode(sid);
1300             addDiscovery(node);
1301         } catch (Exception e) {
1302             ci.println("Please enter a valid number");
1303         }
1304         return;
1305     }
1306
1307     public void _remsw(CommandInterpreter ci) {
1308         String val = ci.nextArgument();
1309         Long sid;
1310         try {
1311             sid = Long.parseLong(val);
1312             Node node = NodeCreator.createOFNode(sid);
1313             removeDiscovery(node);
1314         } catch (Exception e) {
1315             ci.println("Please enter a valid number");
1316         }
1317         return;
1318     }
1319
1320     public void _pthrot(CommandInterpreter ci) {
1321         if (this.throttling) {
1322             ci.println("Throttling is enabled");
1323         } else {
1324             ci.println("Throttling is disabled");
1325         }
1326     }
1327
1328     public void _ethrot(CommandInterpreter ci) {
1329         this.throttling = true;
1330         ci.println("Throttling is enabled");
1331         return;
1332     }
1333
1334     public void _dthrot(CommandInterpreter ci) {
1335         this.throttling = false;
1336         ci.println("Throttling is disabled");
1337         return;
1338     }
1339
1340     @Override
1341     public void updateNode(Node node, UpdateType type, Set<Property> props) {
1342         switch (type) {
1343         case ADDED:
1344             addNode(node, props);
1345             break;
1346         case REMOVED:
1347             removeNode(node);
1348             break;
1349         default:
1350             break;
1351         }
1352     }
1353
1354     @Override
1355     public void updateNodeConnector(NodeConnector nodeConnector, UpdateType type, Set<Property> props) {
1356         Config config = null;
1357         State state = null;
1358         boolean enabled = false;
1359
1360         for (Property prop : props) {
1361             if (prop.getName().equals(Config.ConfigPropName)) {
1362                 config = (Config) prop;
1363             } else if (prop.getName().equals(State.StatePropName)) {
1364                 state = (State) prop;
1365             }
1366         }
1367         enabled = ((config != null) && (config.getValue() == Config.ADMIN_UP) && (state != null) && (state.getValue() == State.EDGE_UP));
1368
1369         switch (type) {
1370         case ADDED:
1371             if (enabled) {
1372                 addDiscovery(nodeConnector);
1373                 logger.trace("ADDED enabled {}", nodeConnector);
1374             } else {
1375                 logger.trace("ADDED disabled {}", nodeConnector);
1376             }
1377             break;
1378         case CHANGED:
1379             if (enabled) {
1380                 addDiscovery(nodeConnector);
1381                 logger.trace("CHANGED enabled {}", nodeConnector);
1382             } else {
1383                 removeDiscovery(nodeConnector);
1384                 logger.trace("CHANGED disabled {}", nodeConnector);
1385             }
1386             break;
1387         case REMOVED:
1388             removeDiscovery(nodeConnector);
1389             logger.trace("REMOVED enabled {}", nodeConnector);
1390             break;
1391         default:
1392             return;
1393         }
1394     }
1395
1396     public void addNode(Node node, Set<Property> props) {
1397         if (node == null) {
1398             return;
1399         }
1400
1401         addDiscovery(node);
1402     }
1403
1404     public void removeNode(Node node) {
1405         if (node == null) {
1406             return;
1407         }
1408
1409         removeDiscovery(node);
1410     }
1411
1412     void setController(IController s) {
1413         this.controller = s;
1414     }
1415
1416     void unsetController(IController s) {
1417         if (this.controller == s) {
1418             this.controller = null;
1419         }
1420     }
1421
1422     public void setInventoryProvider(IInventoryProvider service) {
1423         this.inventoryProvider = service;
1424     }
1425
1426     public void unsetInventoryProvider(IInventoryProvider service) {
1427         this.inventoryProvider = null;
1428     }
1429
1430     public void setIDataPacketMux(IDataPacketMux service) {
1431         this.iDataPacketMux = service;
1432     }
1433
1434     public void unsetIDataPacketMux(IDataPacketMux service) {
1435         if (this.iDataPacketMux == service) {
1436             this.iDataPacketMux = null;
1437         }
1438     }
1439
1440     void setDiscoveryListener(IDiscoveryListener s) {
1441         this.discoveryListener = s;
1442     }
1443
1444     void unsetDiscoveryListener(IDiscoveryListener s) {
1445         if (this.discoveryListener == s) {
1446             this.discoveryListener = null;
1447         }
1448     }
1449
1450     void setIPluginOutConnectionService(IPluginOutConnectionService s) {
1451         connectionOutService = s;
1452     }
1453
1454     void unsetIPluginOutConnectionService(IPluginOutConnectionService s) {
1455         if (connectionOutService == s) {
1456             connectionOutService = null;
1457         }
1458     }
1459
1460     private void initDiscoveryPacket() {
1461         // Create LLDP ChassisID TLV
1462         chassisIdTlv = new LLDPTLV();
1463         chassisIdTlv.setType(LLDPTLV.TLVType.ChassisID.getValue());
1464
1465         // Create LLDP PortID TLV
1466         portIdTlv = new LLDPTLV();
1467         portIdTlv.setType(LLDPTLV.TLVType.PortID.getValue());
1468
1469         // Create LLDP TTL TLV
1470         byte[] ttl = new byte[] { (byte) 0, (byte) 120 };
1471         ttlTlv = new LLDPTLV();
1472         ttlTlv.setType(LLDPTLV.TLVType.TTL.getValue()).setLength((short) ttl.length).setValue(ttl);
1473
1474         customTlv = new LLDPTLV();
1475     }
1476
1477     /**
1478      * Function called by the dependency manager when all the required
1479      * dependencies are satisfied
1480      *
1481      */
1482     void init() {
1483         logger.trace("Init called");
1484
1485         transmitQ = new LinkedBlockingQueue<NodeConnector>();
1486
1487         readyListHi = new CopyOnWriteArrayList<NodeConnector>();
1488         readyListLo = new CopyOnWriteArrayList<NodeConnector>();
1489         stagingList = new CopyOnWriteArrayList<NodeConnector>();
1490         holdTime = new ConcurrentHashMap<NodeConnector, Integer>();
1491         elapsedTime = new ConcurrentHashMap<NodeConnector, Integer>();
1492         edgeMap = new ConcurrentHashMap<NodeConnector, Edge>();
1493         agingMap = new ConcurrentHashMap<NodeConnector, Integer>();
1494         prodMap = new ConcurrentHashMap<NodeConnector, Edge>();
1495         discoverySnoopingDisableList = new CopyOnWriteArrayList<NodeConnector>();
1496
1497         discoveryBatchRestartTicks = getDiscoveryInterval();
1498         discoveryBatchPauseTicks = getDiscoveryPauseInterval();
1499         discoveryTimeoutTicks = getDiscoveryTimeout();
1500         discoveryThresholdTicks = getDiscoveryThreshold();
1501         discoveryAgeoutTicks = getDiscoveryAgeout();
1502         discoveryConsistencyCheckTickCount = discoveryBatchPauseTicks;
1503         discoveryBatchMaxPorts = getDiscoveryBatchMaxPorts();
1504
1505         discoveryTimer = new Timer("DiscoveryService");
1506         discoveryTimerTask = new DiscoveryTimerTask();
1507
1508         transmitThread = new Thread(new DiscoveryTransmit(transmitQ));
1509
1510         initDiscoveryPacket();
1511
1512         registerWithOSGIConsole();
1513     }
1514
1515     /**
1516      * Function called by the dependency manager when at least one dependency
1517      * become unsatisfied or when the component is shutting down because for
1518      * example bundle is being stopped.
1519      *
1520      */
1521     void destroy() {
1522         transmitQ = null;
1523         readyListHi = null;
1524         readyListLo = null;
1525         stagingList = null;
1526         holdTime = null;
1527         edgeMap = null;
1528         agingMap = null;
1529         prodMap = null;
1530         discoveryTimer = null;
1531         discoveryTimerTask = null;
1532         transmitThread = null;
1533     }
1534
1535     /**
1536      * Function called by dependency manager after "init ()" is called and after
1537      * the services provided by the class are registered in the service registry
1538      *
1539      */
1540     void start() {
1541         discoveryTimer.schedule(discoveryTimerTask, discoveryTimerTick, discoveryTimerTick);
1542         transmitThread.start();
1543     }
1544
1545     /**
1546      * Function called after registering the service in OSGi service registry.
1547      */
1548     void started() {
1549         /* get a snapshot of all the existing switches */
1550         addDiscovery();
1551     }
1552
1553     /**
1554      * Function called by the dependency manager before the services exported by
1555      * the component are unregistered, this will be followed by a "destroy ()"
1556      * calls
1557      *
1558      */
1559     void stop() {
1560         shuttingDown = true;
1561         discoveryTimer.cancel();
1562         transmitThread.interrupt();
1563     }
1564
1565     @Override
1566     public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) {
1567     }
1568
1569     @Override
1570     public void containerFlowUpdated(String containerName, ContainerFlow previousFlow, ContainerFlow currentFlow,
1571             UpdateType t) {
1572     }
1573
1574     @Override
1575     public void nodeConnectorUpdated(String containerName, NodeConnector p, UpdateType t) {
1576         switch (t) {
1577         case ADDED:
1578             moveToReadyListHi(p);
1579             break;
1580         default:
1581             break;
1582         }
1583     }
1584
1585     @Override
1586     public void containerModeUpdated(UpdateType t) {
1587         // do nothing
1588     }
1589
1590     private byte[] getSourceMACFromNodeID(String nodeId) {
1591         byte[] cid = HexEncode.bytesFromHexString(nodeId);
1592         byte[] sourceMac = new byte[6];
1593         int pos = cid.length - sourceMac.length;
1594
1595         if (pos >= 0) {
1596             System.arraycopy(cid, pos, sourceMac, 0, sourceMac.length);
1597         }
1598
1599         return sourceMac;
1600     }
1601
1602     private int getDiscoveryTicks(DiscoveryPeriod dp, String val) {
1603         if (dp == null) {
1604             return 0;
1605         }
1606
1607         if (val != null) {
1608             try {
1609                 dp.setTime(Integer.parseInt(val));
1610             } catch (Exception e) {
1611             }
1612         }
1613
1614         return dp.getTick();
1615     }
1616
1617     /**
1618      * This method returns the interval which determines how often the discovery
1619      * packets will be sent.
1620      *
1621      * @return The discovery interval in ticks
1622      */
1623     private int getDiscoveryInterval() {
1624         String intvl = System.getProperty("of.discoveryInterval");
1625         return getDiscoveryTicks(DiscoveryPeriod.INTERVAL, intvl);
1626     }
1627
1628     /**
1629      * This method returns the timeout value in receiving subsequent discovery packets on a port.
1630      *
1631      * @return The discovery timeout in ticks
1632      */
1633     private int getDiscoveryTimeout() {
1634         String val = System.getProperty("of.discoveryTimeoutMultiple");
1635         int multiple = 2;
1636
1637         if (val != null) {
1638             try {
1639                 multiple = Integer.parseInt(val);
1640             } catch (Exception e) {
1641             }
1642         }
1643         return getDiscoveryInterval() * multiple + 3;
1644     }
1645
1646     /**
1647      * This method returns the user configurable threshold value
1648      *
1649      * @return The discovery threshold value in ticks
1650      */
1651     private int getDiscoveryThreshold() {
1652         String val = System.getProperty("of.discoveryThreshold");
1653         return getDiscoveryTicks(DiscoveryPeriod.THRESHOLD, val);
1654     }
1655
1656     /**
1657      * This method returns the discovery entry aging time in ticks.
1658      *
1659      * @return The aging time in ticks
1660      */
1661     private int getDiscoveryAgeout() {
1662         return getDiscoveryTicks(DiscoveryPeriod.AGEOUT, null);
1663     }
1664
1665     /**
1666      * This method returns the pause interval
1667      *
1668      * @return The pause interval in ticks
1669      */
1670     private int getDiscoveryPauseInterval() {
1671         if (discoveryBatchRestartTicks > discoveryBatchPausePeriod) {
1672             return discoveryBatchRestartTicks - discoveryBatchPausePeriod;
1673         } else {
1674             return discoveryBatchRestartTicks - 1;
1675         }
1676     }
1677
1678     /**
1679      * This method returns the user configurable maximum number of ports handled
1680      * in one discovery batch.
1681      *
1682      * @return The maximum number of ports
1683      */
1684     private int getDiscoveryBatchMaxPorts() {
1685         String val = System.getProperty("of.discoveryBatchMaxPorts");
1686         int ports = 512;
1687
1688         if (val != null) {
1689             try {
1690                 ports = Integer.parseInt(val);
1691             } catch (Exception e) {
1692             }
1693         }
1694         return ports;
1695     }
1696 }