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