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