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