3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
10 package org.opendaylight.controller.protocol_plugin.openflow.internal;
12 import java.nio.ByteBuffer;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.Deque;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
20 import java.util.Map.Entry;
22 import java.util.Timer;
23 import java.util.TimerTask;
24 import java.util.concurrent.BlockingQueue;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentMap;
27 import java.util.concurrent.LinkedBlockingDeque;
28 import java.util.concurrent.LinkedBlockingQueue;
30 import org.eclipse.osgi.framework.console.CommandInterpreter;
31 import org.eclipse.osgi.framework.console.CommandProvider;
32 import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
33 import org.opendaylight.controller.protocol_plugin.openflow.IOFInventoryService;
34 import org.opendaylight.controller.protocol_plugin.openflow.IOFStatisticsManager;
35 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
36 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
37 import org.opendaylight.controller.protocol_plugin.openflow.vendorextension.v6extension.V6Match;
38 import org.opendaylight.controller.protocol_plugin.openflow.vendorextension.v6extension.V6StatsReply;
39 import org.opendaylight.controller.protocol_plugin.openflow.vendorextension.v6extension.V6StatsRequest;
40 import org.openflow.protocol.OFError;
41 import org.openflow.protocol.OFMatch;
42 import org.openflow.protocol.OFPort;
43 import org.openflow.protocol.OFStatisticsRequest;
44 import org.openflow.protocol.statistics.OFAggregateStatisticsRequest;
45 import org.openflow.protocol.statistics.OFDescriptionStatistics;
46 import org.openflow.protocol.statistics.OFFlowStatisticsReply;
47 import org.openflow.protocol.statistics.OFFlowStatisticsRequest;
48 import org.openflow.protocol.statistics.OFPortStatisticsReply;
49 import org.openflow.protocol.statistics.OFPortStatisticsRequest;
50 import org.openflow.protocol.statistics.OFQueueStatisticsRequest;
51 import org.openflow.protocol.statistics.OFStatistics;
52 import org.openflow.protocol.statistics.OFStatisticsType;
53 import org.openflow.protocol.statistics.OFVendorStatistics;
54 import org.openflow.util.HexString;
55 import org.osgi.framework.BundleContext;
56 import org.osgi.framework.FrameworkUtil;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 import org.opendaylight.controller.sal.core.Name;
61 import org.opendaylight.controller.sal.core.Node;
62 import org.opendaylight.controller.sal.core.NodeConnector;
63 import org.opendaylight.controller.sal.core.Property;
64 import org.opendaylight.controller.sal.core.UpdateType;
65 import org.opendaylight.controller.sal.utils.GlobalConstants;
66 import org.opendaylight.controller.sal.utils.HexEncode;
67 import org.opendaylight.controller.sal.utils.ServiceHelper;
70 * It periodically polls the different OF statistics from the OF switches
71 * and caches them for quick retrieval for the above layers' modules
72 * It also provides an API to directly query the switch about the statistics
77 public class OFStatisticsManager implements IOFStatisticsManager,
78 IInventoryShimExternalListener, CommandProvider {
79 private static final Logger log = LoggerFactory
80 .getLogger(OFStatisticsManager.class);
81 private static final int initialSize = 64;
82 private static final long flowStatsPeriod = 10000;
83 private static final long descriptionStatsPeriod = 60000;
84 private static final long portStatsPeriod = 5000;
85 private long statisticsTimeout = 4000;
86 private static final long tickPeriod = 1000;
87 private static short statisticsTickNumber = (short) (flowStatsPeriod / tickPeriod);
88 private static short descriptionTickNumber = (short) (descriptionStatsPeriod / tickPeriod);
89 private static short portTickNumber = (short) (portStatsPeriod / tickPeriod);
90 private static short factoredSamples = (short) 2;
91 private static short counter = 1;
92 private IController controller = null;
93 private ConcurrentMap<Long, List<OFStatistics>> flowStatistics;
94 private ConcurrentMap<Long, List<OFStatistics>> descStatistics;
95 private ConcurrentMap<Long, List<OFStatistics>> portStatistics;
96 private List<OFStatistics> dummyList;
97 private ConcurrentMap<Long, StatisticsTicks> statisticsTimerTicks;
98 protected BlockingQueue<StatsRequest> pendingStatsRequests;
99 protected BlockingQueue<Long> switchPortStatsUpdated;
100 private Thread statisticsCollector;
101 private Thread txRatesUpdater;
102 private Timer statisticsTimer;
103 private TimerTask statisticsTimerTask;
104 private ConcurrentMap<Long, Boolean> switchSupportsVendorExtStats;
105 private Map<Long, String> switchNamesDB;
106 private Map<Long, Map<Short, TxRates>> txRates; // Per port sampled (every portStatsPeriod) transmit rate
109 * The object containing the latest factoredSamples tx rate samples
110 * for a given switch port
112 protected class TxRates {
113 Deque<Long> sampledTxBytes; // contains the latest factoredSamples sampled transmitted bytes
116 sampledTxBytes = new LinkedBlockingDeque<Long>();
119 public void update(Long txBytes) {
121 * Based on how many samples our average works on,
122 * we might have to remove the oldest sample
124 if (sampledTxBytes.size() == factoredSamples) {
125 sampledTxBytes.removeLast();
128 // Add the latest sample to the top of the queue
129 sampledTxBytes.addFirst(txBytes);
133 * Returns the average transmit rate in bps
134 * @return the average transmit rate [bps]
136 public long getAverageTxRate() {
139 * If we cannot provide the value for the time window length set
141 if (sampledTxBytes.size() < factoredSamples) {
144 long increment = (long) (sampledTxBytes.getFirst() - sampledTxBytes
146 long timePeriod = (long) (factoredSamples * portStatsPeriod)
148 average = (8 * increment) / timePeriod;
153 public void setController(IController core) {
154 this.controller = core;
157 public void unsetController(IController core) {
158 if (this.controller == core) {
159 this.controller = null;
164 * Function called by the dependency manager when all the required
165 * dependencies are satisfied
172 * Function called by the dependency manager when at least one
173 * dependency become unsatisfied or when the component is shutting
174 * down because for example bundle is being stopped.
181 * Function called by dependency manager after "init ()" is called
182 * and after the services provided by the class are registered in
183 * the service registry
187 // Start managed timers
188 statisticsTimer.scheduleAtFixedRate(statisticsTimerTask, 0, tickPeriod);
190 // Start statistics collector thread
191 statisticsCollector.start();
193 // Start bandwidth utilization computer thread
194 txRatesUpdater.start();
197 registerWithOSGIConsole();
201 * Function called by the dependency manager before the services
202 * exported by the component are unregistered, this will be
203 * followed by a "destroy ()" calls
207 // Stop managed timers
208 statisticsTimer.cancel();
211 public OFStatisticsManager() {
212 flowStatistics = new ConcurrentHashMap<Long, List<OFStatistics>>();
213 descStatistics = new ConcurrentHashMap<Long, List<OFStatistics>>();
214 portStatistics = new ConcurrentHashMap<Long, List<OFStatistics>>();
215 dummyList = new ArrayList<OFStatistics>(1);
216 switchNamesDB = new HashMap<Long, String>();
217 statisticsTimerTicks = new ConcurrentHashMap<Long, StatisticsTicks>(
219 pendingStatsRequests = new LinkedBlockingQueue<StatsRequest>(
221 switchPortStatsUpdated = new LinkedBlockingQueue<Long>(initialSize);
222 switchSupportsVendorExtStats = new ConcurrentHashMap<Long, Boolean>(
224 txRates = new HashMap<Long, Map<Short, TxRates>>(initialSize);
226 // Initialize managed timers
227 statisticsTimer = new Timer();
228 statisticsTimerTask = new TimerTask() {
235 // Initialize Statistics collector thread
236 statisticsCollector = new Thread(new Runnable() {
241 StatsRequest req = pendingStatsRequests.take();
242 acquireStatistics(req.switchId, req.type,
244 } catch (InterruptedException e) {
246 .warn("Flow Statistics Collector thread interrupted");
250 }, "Statistics Collector");
252 // Initialize Tx Rate Updater thread
253 txRatesUpdater = new Thread(new Runnable() {
258 long switchId = switchPortStatsUpdated.take();
259 updatePortsTxRate(switchId);
260 } catch (InterruptedException e) {
261 log.warn("TX Rate Updater thread interrupted");
265 }, "TX Rate Updater");
269 private void registerWithOSGIConsole() {
270 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
272 bundleContext.registerService(CommandProvider.class.getName(), this,
276 private static class StatsRequest {
277 protected Long switchId;
278 protected OFStatisticsType type;
280 public StatsRequest(Long d, OFStatisticsType t) {
285 public String toString() {
286 return "SReq = {switchId=" + switchId + ", type=" + type + "}";
290 public int hashCode() {
291 final int prime = 31;
293 result = prime * result
294 + ((switchId == null) ? 0 : switchId.hashCode());
295 result = prime * result + ((type == null) ? 0 : type.ordinal());
300 public boolean equals(Object obj) {
307 if (getClass() != obj.getClass()) {
310 StatsRequest other = (StatsRequest) obj;
311 if (switchId == null) {
312 if (other.switchId != null) {
315 } else if (!switchId.equals(other.switchId)) {
318 if (type != other.type) {
325 private void addStatisticsTicks(Long switchId) {
326 switchSupportsVendorExtStats.put(switchId, Boolean.TRUE); // Assume switch supports Vendor extension stats
327 statisticsTimerTicks.put(switchId, new StatisticsTicks(true));
328 log.info("Added Switch {} to target pool", HexString
329 .toHexString(switchId.longValue()));
332 protected static class StatisticsTicks {
333 private short flowStatisticsTicks;
334 private short descriptionTicks;
335 private short portStatisticsTicks;
337 public StatisticsTicks(boolean scattered) {
339 // scatter bursts by statisticsTickPeriod
342 } // being paranoid here
343 flowStatisticsTicks = (short) (1 + counter
344 % statisticsTickNumber);
345 descriptionTicks = (short) (1 + counter % descriptionTickNumber);
346 portStatisticsTicks = (short) (1 + counter % portTickNumber);
348 flowStatisticsTicks = statisticsTickNumber;
349 descriptionTicks = descriptionTickNumber;
350 portStatisticsTicks = portTickNumber;
354 public boolean decrementFlowTicksIsZero() {
355 // Please ensure no code is inserted between the if check and the flowStatisticsTicks reset
356 if (--flowStatisticsTicks == 0) {
357 flowStatisticsTicks = statisticsTickNumber;
363 public boolean decrementDescTicksIsZero() {
364 // Please ensure no code is inserted between the if check and the descriptionTicks reset
365 if (--descriptionTicks == 0) {
366 descriptionTicks = descriptionTickNumber;
372 public boolean decrementPortTicksIsZero() {
373 // Please ensure no code is inserted between the if check and the descriptionTicks reset
374 if (--portStatisticsTicks == 0) {
375 portStatisticsTicks = portTickNumber;
381 public String toString() {
382 return "{fT=" + flowStatisticsTicks + ",dT=" + descriptionTicks
383 + ",pT=" + portStatisticsTicks + "}";
387 private void printInfoMessage(String type, StatsRequest request) {
391 + " stats request not inserted for switch: {}. Queue size: {}. Collector state: {}.",
392 new Object[] { HexString.toHexString(request.switchId),
393 pendingStatsRequests.size(),
394 statisticsCollector.getState().toString() });
397 protected void decrementTicks() {
398 StatsRequest request = null;
399 for (Map.Entry<Long, StatisticsTicks> entry : statisticsTimerTicks
401 StatisticsTicks clock = entry.getValue();
402 Long switchId = entry.getKey();
403 if (clock.decrementFlowTicksIsZero() == true) {
404 request = (switchSupportsVendorExtStats.get(switchId) == Boolean.TRUE) ? new StatsRequest(
405 switchId, OFStatisticsType.VENDOR)
406 : new StatsRequest(switchId, OFStatisticsType.FLOW);
407 // If a request for this switch is already in the queue, skip to add this new request
408 if (!pendingStatsRequests.contains(request)
409 && false == pendingStatsRequests.offer(request)) {
410 printInfoMessage("Flow", request);
414 if (clock.decrementDescTicksIsZero() == true) {
415 request = new StatsRequest(switchId, OFStatisticsType.DESC);
416 // If a request for this switch is already in the queue, skip to add this new request
417 if (!pendingStatsRequests.contains(request)
418 && false == pendingStatsRequests.offer(request)) {
419 printInfoMessage("Description", request);
423 if (clock.decrementPortTicksIsZero() == true) {
424 request = new StatsRequest(switchId, OFStatisticsType.PORT);
425 // If a request for this switch is already in the queue, skip to add this new request
426 if (!pendingStatsRequests.contains(request)
427 && false == pendingStatsRequests.offer(request)) {
428 printInfoMessage("Port", request);
434 private void removeStatsRequestTasks(Long switchId) {
435 log.info("Cleaning Statistics database for switch "
436 + HexEncode.longToHexString(switchId));
437 // To be safe, let's attempt removal of both VENDOR and FLOW request. It does not hurt
438 pendingStatsRequests.remove(new StatsRequest(switchId,
439 OFStatisticsType.VENDOR));
440 pendingStatsRequests.remove(new StatsRequest(switchId,
441 OFStatisticsType.FLOW));
442 pendingStatsRequests.remove(new StatsRequest(switchId,
443 OFStatisticsType.DESC));
444 pendingStatsRequests.remove(new StatsRequest(switchId,
445 OFStatisticsType.PORT));
446 // Take care of the TX rate databases
447 switchPortStatsUpdated.remove(switchId);
448 txRates.remove(switchId);
451 private void clearFlowStatsAndTicks(Long switchId) {
452 statisticsTimerTicks.remove(switchId);
453 removeStatsRequestTasks(switchId);
454 flowStatistics.remove(switchId);
455 log.info("Statistics removed for switch "
456 + HexString.toHexString(switchId));
459 private void acquireStatistics(Long switchId, OFStatisticsType statType,
462 // Query the switch on all matches
463 List<OFStatistics> values = this.acquireStatistics(switchId, statType,
466 // Update local caching database if got a valid response
467 if (values != null && !values.isEmpty()) {
468 if ((statType == OFStatisticsType.FLOW)
469 || (statType == OFStatisticsType.VENDOR)) {
470 flowStatistics.put(switchId, values);
471 } else if (statType == OFStatisticsType.DESC) {
472 if ((switchNamesDB != null)
473 && switchNamesDB.containsKey(switchId)) {
474 // Check if user manually configured a name for the switch
475 for (OFStatistics entry : values) {
476 OFDescriptionStatistics reply = (OFDescriptionStatistics) entry;
477 reply.setSerialNumber(switchNamesDB.get(switchId));
480 // check if notification is needed
481 if (descStatistics.get(switchId) == null
482 || !(descStatistics.get(switchId).get(0).equals(values
484 IOFInventoryService inventory = (IOFInventoryService) ServiceHelper
485 .getInstance(IOFInventoryService.class,
486 GlobalConstants.DEFAULT.toString(), this);
487 if (inventory != null) {
488 // Notify Inventory Service about the name update
489 Set<Property> propSet = new HashSet<Property>(1);
490 Name name = new Name(((OFDescriptionStatistics) values
491 .get(0)).getSerialNumber());
493 inventory.updateSwitchProperty(switchId, propSet);
497 descStatistics.put(switchId, values);
498 } else if (statType == OFStatisticsType.PORT) {
499 // Overwrite cache with new port statistics for this switch
500 portStatistics.put(switchId, values);
502 // Wake up the thread which maintains the TX byte counters for each port
503 switchPortStatsUpdated.offer(switchId);
509 * Generic function to get the statistics form a OF switch
511 @SuppressWarnings("unchecked")
512 private List<OFStatistics> acquireStatistics(Long switchId,
513 OFStatisticsType statsType, Object target, long timeout) {
514 List<OFStatistics> values = null;
516 ISwitch sw = controller.getSwitch(switchId);
519 OFStatisticsRequest req = new OFStatisticsRequest();
520 req.setStatisticType(statsType);
521 int requestLength = req.getLengthU();
523 if (statsType == OFStatisticsType.FLOW) {
524 OFMatch match = null;
525 if (target == null) {
527 match = new OFMatch();
528 match.setWildcards(0xffffffff);
529 } else if (!(target instanceof OFMatch)) {
531 log.warn("Invalid target type for Flow stats request: "
532 + target.getClass());
535 // Specific flow request
536 match = (OFMatch) target;
538 OFFlowStatisticsRequest specificReq = new OFFlowStatisticsRequest();
539 specificReq.setMatch(match);
540 specificReq.setOutPort(OFPort.OFPP_NONE.getValue());
541 specificReq.setTableId((byte) 0xff);
542 req.setStatistics(Collections
543 .singletonList((OFStatistics) specificReq));
544 requestLength += specificReq.getLength();
546 } else if (statsType == OFStatisticsType.VENDOR) {
547 V6StatsRequest specificReq = new V6StatsRequest();
548 specificReq.setOutPort(OFPort.OFPP_NONE.getValue());
549 specificReq.setTableId((byte) 0xff);
550 req.setStatistics(Collections
551 .singletonList((OFStatistics) specificReq));
552 requestLength += specificReq.getLength();
554 } else if (statsType == OFStatisticsType.AGGREGATE) {
555 OFAggregateStatisticsRequest specificReq = new OFAggregateStatisticsRequest();
556 OFMatch match = new OFMatch();
557 match.setWildcards(0xffffffff);
558 specificReq.setMatch(match);
559 specificReq.setOutPort(OFPort.OFPP_NONE.getValue());
560 specificReq.setTableId((byte) 0xff);
561 req.setStatistics(Collections
562 .singletonList((OFStatistics) specificReq));
563 requestLength += specificReq.getLength();
565 } else if (statsType == OFStatisticsType.PORT) {
567 if (target == null) {
569 targetPort = (short) OFPort.OFPP_NONE.getValue();
570 } else if (!(target instanceof Short)) {
572 log.warn("Invalid target type for Port stats request: "
573 + target.getClass());
576 // Specific port request
577 targetPort = (Short) target;
579 OFPortStatisticsRequest specificReq = new OFPortStatisticsRequest();
580 specificReq.setPortNumber(targetPort);
581 req.setStatistics(Collections
582 .singletonList((OFStatistics) specificReq));
583 requestLength += specificReq.getLength();
585 } else if (statsType == OFStatisticsType.QUEUE) {
586 OFQueueStatisticsRequest specificReq = new OFQueueStatisticsRequest();
587 specificReq.setPortNumber((short) OFPort.OFPP_ALL.getValue());
588 specificReq.setQueueId(0xffffffff);
589 req.setStatistics(Collections
590 .singletonList((OFStatistics) specificReq));
591 requestLength += specificReq.getLength();
593 } else if (statsType == OFStatisticsType.DESC) {
595 } else if (statsType == OFStatisticsType.TABLE) {
598 req.setLengthU(requestLength);
599 Object result = sw.getStatistics(req);
601 if (result == null) {
602 log.warn("Request Timed Out for ({}) from switch {}", type,
603 HexString.toHexString(switchId));
604 } else if (result instanceof OFError) {
605 log.warn("Switch {} failed to handle ({}) stats request: "
606 + Utils.getOFErrorString((OFError) result), HexString
607 .toHexString(switchId), type);
608 if (this.switchSupportsVendorExtStats.get(switchId) == Boolean.TRUE) {
611 "Switching back to regular Flow stats requests for switch {}",
612 HexString.toHexString(switchId));
613 this.switchSupportsVendorExtStats.put(switchId,
617 values = (List<OFStatistics>) result;
624 public List<OFStatistics> getOFFlowStatistics(Long switchId) {
625 List<OFStatistics> list = flowStatistics.get(switchId);
628 * Check on emptiness as interference between add and get is still
629 * possible on the inner list (the concurrentMap entry's value)
631 return (list == null || list.isEmpty()) ? this.dummyList
632 : (list.get(0) instanceof OFVendorStatistics) ? this
633 .v6StatsListToOFStatsList(list) : list;
637 public List<OFStatistics> getOFFlowStatistics(Long switchId, OFMatch ofMatch) {
638 List<OFStatistics> statsList = flowStatistics.get(switchId);
641 * Check on emptiness as interference between add and get is still
642 * possible on the inner list (the concurrentMap entry's value)
644 if (statsList == null || statsList.isEmpty()) {
645 return this.dummyList;
648 if (statsList.get(0) instanceof OFVendorStatistics) {
650 * Caller could provide regular OF match when we
651 * instead pull the vendor statistics from this node
652 * Caller is not supposed to know whether this switch supports
653 * vendor extensions statistics requests
655 V6Match targetMatch = (ofMatch instanceof V6Match) ? (V6Match) ofMatch
656 : new V6Match(ofMatch);
658 List<OFStatistics> targetList = v6StatsListToOFStatsList(statsList);
659 for (OFStatistics stats : targetList) {
660 V6StatsReply v6Stats = (V6StatsReply) stats;
661 V6Match v6Match = v6Stats.getMatch();
662 if (v6Match.equals(targetMatch)) {
663 List<OFStatistics> list = new ArrayList<OFStatistics>();
669 for (OFStatistics stats : statsList) {
670 OFFlowStatisticsReply flowStats = (OFFlowStatisticsReply) stats;
671 if (flowStats.getMatch().equals(ofMatch)) {
672 List<OFStatistics> list = new ArrayList<OFStatistics>();
678 return this.dummyList;
682 * Converts the v6 vendor statistics to the OFStatistics
684 private List<OFStatistics> v6StatsListToOFStatsList(
685 List<OFStatistics> statistics) {
686 List<OFStatistics> v6statistics = new ArrayList<OFStatistics>();
687 if (statistics != null && !statistics.isEmpty()) {
688 for (OFStatistics stats : statistics) {
689 if (stats instanceof OFVendorStatistics) {
690 List<OFStatistics> r = getV6ReplyStatistics((OFVendorStatistics) stats);
692 v6statistics.addAll(r);
700 private static List<OFStatistics> getV6ReplyStatistics(
701 OFVendorStatistics stat) {
702 int length = stat.getLength();
703 List<OFStatistics> results = new ArrayList<OFStatistics>();
705 return null; // Nicira Hdr is 12 bytes. We need atleast that much
706 ByteBuffer data = ByteBuffer.allocate(length);
709 log.trace("getV6ReplyStatistics: Buffer BYTES ARE {}", HexString
710 .toHexString(data.array()));
712 int vendor = data.getInt(); //first 4 bytes is vendor id.
713 if (vendor != V6StatsRequest.NICIRA_VENDOR_ID) {
715 .debug("Unexpected vendor id: 0x{}", Integer
716 .toHexString(vendor));
719 //go ahead by 8 bytes which is 8 bytes of 0
720 data.getLong(); //should be all 0's
721 length -= 12; // 4 bytes Nicira Hdr + 8 bytes from above line have been consumed
724 V6StatsReply v6statsreply;
727 v6statsreply = new V6StatsReply();
728 min_len = v6statsreply.getLength();
729 if (length < v6statsreply.getLength())
731 v6statsreply.setActionFactory(stat.getActionFactory());
732 v6statsreply.readFrom(data);
733 if (v6statsreply.getLength() < min_len)
735 v6statsreply.setVendorId(vendor);
736 log.trace("V6StatsReply: {}", v6statsreply);
737 length -= v6statsreply.getLength();
738 results.add(v6statsreply);
744 public List<OFStatistics> queryStatistics(Long switchId,
745 OFStatisticsType statType, Object target, long timeout) {
747 * Caller does not know and it is not supposed to know whether
748 * this switch supports vendor extension. We adjust the target for him
750 if (statType == OFStatisticsType.FLOW) {
751 if (switchSupportsVendorExtStats.get(switchId) == Boolean.TRUE) {
752 statType = OFStatisticsType.VENDOR;
756 List<OFStatistics> list = this.acquireStatistics(switchId, statType,
759 return (list == null) ? null
760 : (statType == OFStatisticsType.VENDOR) ? v6StatsListToOFStatsList(list)
765 public List<OFStatistics> getOFDescStatistics(Long switchId) {
766 if (!descStatistics.containsKey(switchId))
767 return this.dummyList;
769 return descStatistics.get(switchId);
773 public List<OFStatistics> getOFPortStatistics(Long switchId) {
774 if (!portStatistics.containsKey(switchId)) {
775 return this.dummyList;
778 return portStatistics.get(switchId);
782 public List<OFStatistics> getOFPortStatistics(Long switchId, short portId) {
783 if (!portStatistics.containsKey(switchId)) {
784 return this.dummyList;
786 List<OFStatistics> list = new ArrayList<OFStatistics>(1);
787 for (OFStatistics stats : portStatistics.get(switchId)) {
788 if (((OFPortStatisticsReply) stats).getPortNumber() == portId) {
797 public int getFlowsNumber(long switchId) {
798 return this.flowStatistics.get(switchId).size();
802 * InventoryShim replay for us all the switch addition which happened before we were brought up
805 public void updateNode(Node node, UpdateType type, Set<Property> props) {
806 Long switchId = (Long) node.getID();
809 addStatisticsTicks(switchId);
812 clearFlowStatsAndTicks(switchId);
818 public void updateNodeConnector(NodeConnector nodeConnector,
819 UpdateType type, Set<Property> props) {
824 * Update the cached port rates for this switch with the latest
825 * retrieved port transmit byte count
828 private synchronized void updatePortsTxRate(long switchId) {
829 List<OFStatistics> newPortStatistics = this.portStatistics
831 if (newPortStatistics == null) {
834 Map<Short, TxRates> rates = this.txRates.get(switchId);
836 // First time rates for this switch are added
837 rates = new HashMap<Short, TxRates>();
838 txRates.put(switchId, rates);
840 for (OFStatistics stats : newPortStatistics) {
841 OFPortStatisticsReply newPortStat = (OFPortStatisticsReply) stats;
842 short port = newPortStat.getPortNumber();
843 TxRates portRatesHolder = rates.get(port);
844 if (portRatesHolder == null) {
845 // First time rates for this port are added
846 portRatesHolder = new TxRates();
847 rates.put(port, portRatesHolder);
849 // Get and store the number of transmitted bytes for this port
850 // And handle the case where agent does not support the counter
851 long transmitBytes = newPortStat.getTransmitBytes();
852 long value = (transmitBytes < 0) ? 0 : transmitBytes;
853 portRatesHolder.update(value);
858 public synchronized long getTransmitRate(Long switchId, Short port) {
860 if (switchId == null || port == null) {
863 Map<Short, TxRates> perSwitch = txRates.get(switchId);
864 if (perSwitch == null) {
867 TxRates portRates = perSwitch.get(port);
868 if (portRates == null) {
871 return portRates.getAverageTxRate();
875 * Manual switch name configuration code
878 public String getHelp() {
879 StringBuffer help = new StringBuffer();
880 help.append("---OF Switch ID to Name Mapping---\n");
882 .append("\t ofaddname - Add a switchId to name mapping entry\n");
884 .append("\t ofdeletename - Delete a switchId from the mapping db\n");
885 help.append("\t ofprintnames - Print the mapping db\n");
886 help.append("---OF Statistics Manager utilities---\n");
888 .append("\t ofdumpstatsmgr - Print Internal Stats Mgr db\n");
890 .append("\t ofstatstimeout - Change Statistics request's timeout value\n");
891 return help.toString();
894 private boolean isValidSwitchId(String switchId) {
895 String regexDatapathID = "^([0-9a-fA-F]{1,2}[:-]){7}[0-9a-fA-F]{1,2}$";
896 String regexDatapathIDLong = "^[0-9a-fA-F]{1,16}$";
898 return (switchId != null && (switchId.matches(regexDatapathID) || switchId
899 .matches(regexDatapathIDLong)));
902 public long getSwitchIDLong(String switchId) {
904 String switchString = "0";
906 if (isValidSwitchId(switchId)) {
907 if (switchId.contains(":")) {
908 // Handle the 00:00:AA:BB:CC:DD:EE:FF notation
909 switchString = switchId.replace(":", "");
910 } else if (switchId.contains("-")) {
911 // Handle the 00-00-AA-BB-CC-DD-EE-FF notation
912 switchString = switchId.replace("-", "");
914 // Handle the 0123456789ABCDEF notation
915 switchString = switchId;
918 return Long.parseLong(switchString, radix);
921 public void _ofaddname(CommandInterpreter ci) {
922 if (switchNamesDB == null)
923 switchNamesDB = new HashMap<Long, String>();
924 String switchId = ci.nextArgument();
925 if (!isValidSwitchId(switchId)) {
926 ci.println("Please provide a valid SwithcId");
929 Long sid = getSwitchIDLong(switchId);
930 String switchName = ci.nextArgument();
931 if (switchName == null) {
932 ci.println("Please provide a valid Switch name");
935 switchNamesDB.put(sid, switchName);
938 public void _ofdeletename(CommandInterpreter ci) {
939 if (switchNamesDB == null)
941 String switchId = ci.nextArgument();
942 if (!isValidSwitchId(switchId)) {
943 ci.println("Please provide a valid SwitchId");
946 Long sid = getSwitchIDLong(switchId);
947 switchNamesDB.remove(sid);
950 public void _ofprintnames(CommandInterpreter ci) {
951 if (switchNamesDB == null)
953 for (Long key : switchNamesDB.keySet()) {
954 ci.println(key + " -> " + switchNamesDB.get(key) + "\n");
959 * Internal information dump code
961 private String prettyPrintSwitchMap(ConcurrentMap<Long, StatisticsTicks> map) {
962 StringBuffer buffer = new StringBuffer();
964 for (Entry<Long, StatisticsTicks> entry : map.entrySet()) {
965 buffer.append(HexString.toHexString(entry.getKey()) + "="
966 + entry.getValue().toString() + " ");
969 return buffer.toString();
972 public void _ofdumpstatsmgr(CommandInterpreter ci) {
973 ci.println("Global Counter: " + counter);
975 .println("Timer Ticks: "
976 + prettyPrintSwitchMap(statisticsTimerTicks));
977 ci.println("PendingStatsQueue: " + pendingStatsRequests);
978 ci.println("PendingStatsQueue size: " + pendingStatsRequests.size());
979 ci.println("Stats Collector alive: " + statisticsCollector.isAlive());
980 ci.println("Stats Collector State: "
981 + statisticsCollector.getState().toString());
982 ci.println("StatsTimer: " + statisticsTimer.toString());
983 ci.println("Flow Stats Period: " + statisticsTickNumber + " s");
984 ci.println("Desc Stats Period: " + descriptionTickNumber + " s");
985 ci.println("Port Stats Period: " + portTickNumber + " s");
986 ci.println("Stats request timeout: " + Float.valueOf(statisticsTimeout)
990 public void _ofstatstimeout(CommandInterpreter ci) {
991 String timeoutString = ci.nextArgument();
992 if (timeoutString == null || !timeoutString.matches("^[0-9]+$")) {
993 ci.println("Invalid value. Has to be numeric.");
997 long newTimeout = Long.valueOf(timeoutString);
998 if (newTimeout < 50 || newTimeout > 60000) {
999 ci.println("Invalid value. Valid range is [50-60000]ms");
1002 this.statisticsTimeout = newTimeout;
1003 ci.println("New value: " + statisticsTimeout + " ms");
1006 public void _resetSwitchCapability(CommandInterpreter ci) {
1007 String sidString = ci.nextArgument();
1009 if (sidString == null) {
1010 ci.println("Insert the switch id (numeric value)");
1014 sid = Long.valueOf(sidString);
1015 this.switchSupportsVendorExtStats.put(sid, Boolean.TRUE);
1016 ci.println("Vendor capability for switch " + sid + " set to "
1017 + this.switchSupportsVendorExtStats.get(sid));
1018 } catch (NumberFormatException e) {
1019 ci.println("Invalid switch id. Has to be numeric.");
1024 public void _ofbw(CommandInterpreter ci) {
1025 String sidString = ci.nextArgument();
1027 if (sidString == null) {
1028 ci.println("Insert the switch id (numeric value)");
1032 sid = Long.valueOf(sidString);
1033 } catch (NumberFormatException e) {
1034 ci.println("Invalid switch id. Has to be numeric.");
1037 Map<Short, TxRates> thisSwitchRates = txRates.get(sid);
1038 ci.println("Bandwidth utilization (" + factoredSamples
1039 * portTickNumber + " sec average) for switch "
1040 + HexEncode.longToHexString(sid) + ":");
1041 if (thisSwitchRates == null) {
1042 ci.println("Not available");
1044 for (Entry<Short, TxRates> entry : thisSwitchRates.entrySet()) {
1045 ci.println("Port: " + entry.getKey() + ": "
1046 + entry.getValue().getAverageTxRate() + " bps");
1052 public void _txratewindow(CommandInterpreter ci) {
1053 String averageWindow = ci.nextArgument();
1055 if (averageWindow == null) {
1057 .println("Insert the length in seconds of the average window for tx rate");
1059 .println("Current: " + factoredSamples * portTickNumber
1064 seconds = Short.valueOf(averageWindow);
1065 } catch (NumberFormatException e) {
1066 ci.println("Invalid period.");
1068 OFStatisticsManager.factoredSamples = (short) (seconds / portTickNumber);
1069 ci.println("New: " + factoredSamples * portTickNumber + " secs");