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