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