diff --git a/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java b/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java index 46d41fc7986d1ab5e7d36c1f7bd4b70f74d4f905..d9a3f462fcc24906bb8fc52f3962b78cc1a161be 100644 --- a/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java +++ b/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java @@ -13,4 +13,6 @@ public interface IStatisticsService extends IFloodlightService { public SwitchPortBandwidth getBandwidthConsumption(DatapathId dpid, OFPort p); public Map<NodePortTuple, SwitchPortBandwidth> getBandwidthConsumption(); + + public void collectStatistics(boolean collect); } diff --git a/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java b/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java index 4ee967c562882427817fd78ce7ca8f8f54547634..e60198481a54d89eacd472c8e927c30d27dc1575 100644 --- a/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java +++ b/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.projectfloodlight.openflow.protocol.OFPortStatsEntry; @@ -47,10 +48,16 @@ public class StatisticsCollector implements IFloodlightModule, IStatisticsServic private static IThreadPoolService threadPoolService; private static IRestApiService restApiService; + private static boolean isEnabled = false; + private static int portStatsInterval = 10; + private static ScheduledFuture<?> portStatsCollector; private static final long BITS_PER_BYTE = 8; private static final long MILLIS_PER_SEC = 1000; + + private static final String INTERVAL_PORT_STATS_STR = "collectionIntervalPortStatsSeconds"; + private static final String ENABLED_STR = "enable"; private static final HashMap<NodePortTuple, SwitchPortBandwidth> portStats = new HashMap<NodePortTuple, SwitchPortBandwidth>(); private static final HashMap<NodePortTuple, SwitchPortBandwidth> tentativePortStats = new HashMap<NodePortTuple, SwitchPortBandwidth>(); @@ -168,14 +175,33 @@ public class StatisticsCollector implements IFloodlightModule, IStatisticsServic threadPoolService = context.getServiceImpl(IThreadPoolService.class); restApiService = context.getServiceImpl(IRestApiService.class); - //TODO Map<String, String> config = context.getConfigParams(this); + Map<String, String> config = context.getConfigParams(this); + if (config.containsKey(ENABLED_STR)) { + try { + isEnabled = Boolean.parseBoolean(config.get(ENABLED_STR).trim()); + } catch (Exception e) { + log.error("Could not parse '{}'. Using default of {}", ENABLED_STR, isEnabled); + } + } + log.info("Statistics collection {}", isEnabled ? "enabled" : "disabled"); + + if (config.containsKey(INTERVAL_PORT_STATS_STR)) { + try { + portStatsInterval = Integer.parseInt(config.get(INTERVAL_PORT_STATS_STR).trim()); + } catch (Exception e) { + log.error("Could not parse '{}'. Using default of {}", INTERVAL_PORT_STATS_STR, portStatsInterval); + } + } + log.info("Port statistics collection interval set to {}s", portStatsInterval); } @Override public void startUp(FloodlightModuleContext context) throws FloodlightModuleException { - threadPoolService.getScheduledExecutor().scheduleAtFixedRate(new PortStatsCollector(), portStatsInterval, portStatsInterval, TimeUnit.SECONDS); restApiService.addRestletRoutable(new SwitchStatisticsWebRoutable()); + if (isEnabled) { + startStatisticsCollection(); + } } @Override @@ -188,6 +214,32 @@ public class StatisticsCollector implements IFloodlightModule, IStatisticsServic public Map<NodePortTuple, SwitchPortBandwidth> getBandwidthConsumption() { return Collections.unmodifiableMap(portStats); } + + @Override + public synchronized void collectStatistics(boolean collect) { + if (collect && !isEnabled) { + startStatisticsCollection(); + isEnabled = true; + } else if (!collect && isEnabled) { + stopStatisticsCollection(); + isEnabled = false; + } + /* otherwise, state is not changing; no-op */ + } + + private void startStatisticsCollection() { + portStatsCollector = threadPoolService.getScheduledExecutor().scheduleAtFixedRate(new PortStatsCollector(), portStatsInterval, portStatsInterval, TimeUnit.SECONDS); + tentativePortStats.clear(); /* must clear out, otherwise might have huge BW result if present and wait a long time before re-enabling stats */ + log.warn("Statistics collection thread(s) started"); + } + + private void stopStatisticsCollection() { + if (!portStatsCollector.cancel(false)) { + log.error("Could not cancel port stats thread"); + } else { + log.warn("Statistics collection thread(s) stopped"); + } + } private Map<DatapathId, List<OFStatsReply>> getSwitchStatistics(Set<DatapathId> dpids, OFStatsType statsType) { HashMap<DatapathId, List<OFStatsReply>> model = new HashMap<DatapathId, List<OFStatsReply>>(); diff --git a/src/main/java/net/floodlightcontroller/statistics/web/ConfigResource.java b/src/main/java/net/floodlightcontroller/statistics/web/ConfigResource.java new file mode 100644 index 0000000000000000000000000000000000000000..881f256c85c042d5e5776dc991f89b9d880cbf2e --- /dev/null +++ b/src/main/java/net/floodlightcontroller/statistics/web/ConfigResource.java @@ -0,0 +1,30 @@ +package net.floodlightcontroller.statistics.web; + +import java.util.Collections; + +import net.floodlightcontroller.statistics.IStatisticsService; + +import org.restlet.resource.Post; +import org.restlet.resource.Put; +import org.restlet.resource.ServerResource; + +public class ConfigResource extends ServerResource { + + @Post + @Put + public Object config() { + IStatisticsService statisticsService = (IStatisticsService) getContext().getAttributes().get(IStatisticsService.class.getCanonicalName()); + + if (getReference().getPath().contains(SwitchStatisticsWebRoutable.ENABLE_STR)) { + statisticsService.collectStatistics(true); + return Collections.singletonMap("statistics-collection", "enabled"); + } + + if (getReference().getPath().contains(SwitchStatisticsWebRoutable.DISABLE_STR)) { + statisticsService.collectStatistics(false); + return Collections.singletonMap("statistics-collection", "disabled"); + } + + return Collections.singletonMap("ERROR", "Unimplemented configuration option"); + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/statistics/web/SwitchStatisticsWebRoutable.java b/src/main/java/net/floodlightcontroller/statistics/web/SwitchStatisticsWebRoutable.java index 748cb5f3e96829f6310d984b38dedaf42f49f33e..378d696f5e85504cd9c0a732268f5eda45bf9b79 100644 --- a/src/main/java/net/floodlightcontroller/statistics/web/SwitchStatisticsWebRoutable.java +++ b/src/main/java/net/floodlightcontroller/statistics/web/SwitchStatisticsWebRoutable.java @@ -9,11 +9,15 @@ import org.restlet.routing.Router; public class SwitchStatisticsWebRoutable implements RestletRoutable { protected static final String DPID_STR = "dpid"; protected static final String PORT_STR = "port"; + protected static final String ENABLE_STR = "enable"; + protected static final String DISABLE_STR = "disable"; @Override public Router getRestlet(Context context) { Router router = new Router(context); router.attach("/bandwidth/{" + DPID_STR + "}/{" + PORT_STR + "}/json", BandwidthResource.class); + router.attach("/config/enable/json", ConfigResource.class); + router.attach("/config/disable/json", ConfigResource.class); return router; } diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties index 9d40c9525174875c6b1e70909e8ce8e7855d346c..29c197ea25579339630c695ac21b9c7ed8061ec8 100644 --- a/src/main/resources/floodlightdefault.properties +++ b/src/main/resources/floodlightdefault.properties @@ -42,3 +42,5 @@ net.floodlightcontroller.restserver.RestApiServer.useHttps=NO net.floodlightcontroller.restserver.RestApiServer.useHttp=YES net.floodlightcontroller.restserver.RestApiServer.httpsPort=8081 net.floodlightcontroller.restserver.RestApiServer.httpPort=8080 +net.floodlightcontroller.statistics.StatisticsCollector.enable=TRUE +net.floodlightcontroller.statistics.StatisticsCollector.collectionIntervalPortStatsSeconds=10 \ No newline at end of file