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