Added a framework for metrics
Added a metrics framework based on the codahale Metrics
package. ONOS can create a registry and maintain Metrics,
as well as dump out metrics values via a REST API. Unit
tests are include that test the REST APIs. Currently
supports Timers, Counters, Gauges, Meters, and Histograms.
Change-Id: I0f6ed87f889dc7037caf9aefc92e663702c6dda8
diff --git a/src/main/java/net/onrc/onos/core/metrics/MetricsObjectResource.java b/src/main/java/net/onrc/onos/core/metrics/MetricsObjectResource.java
new file mode 100644
index 0000000..efb0213
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/MetricsObjectResource.java
@@ -0,0 +1,283 @@
+package net.onrc.onos.core.metrics;
+
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Timer;
+import com.codahale.metrics.Histogram;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import java.util.List;
+
+/**
+ * Resource class to hold Metrics information. Timers, Gauges, Counters,
+ * Meters and Historgrams are currently implemented.
+ */
+@JsonSerialize(using = MetricsObjectSerializer.class)
+@SuppressWarnings("rawtypes")
+public class MetricsObjectResource {
+
+ /**
+ * Base Metric object that all metrics inherit from. Defines common
+ * attributes.
+ */
+ static class BaseMetricObject {
+ private final String name;
+
+ /**
+ * Constructor for the base object. Sets the name attribute.
+ *
+ * @param newName name of the Metric
+ */
+ protected BaseMetricObject(final String newName) {
+ name = newName;
+ }
+
+ /**
+ * Get the name of the Metric.
+ *
+ * @return metric name
+ */
+ public String getName() {
+ return name;
+ }
+ }
+
+ /**
+ * Metric object that represents a Timer.
+ */
+ static class TimerObjectResource extends BaseMetricObject {
+ private final Timer timer;
+
+ /**
+ * Construct a new Timer resource object.
+ *
+ * @param newName name to use for the timer
+ * @param newTimer Metrics Timer object
+ */
+ public TimerObjectResource(final String newName,
+ final Timer newTimer) {
+ super(newName);
+ timer = newTimer;
+ }
+
+ /**
+ * Get the Metrics Timer object for this resource.
+ *
+ * @return Metrics Timer object.
+ */
+ public Timer getTimer() {
+ return timer;
+ }
+ }
+
+ /**
+ * Metric object that represents a Gauge.
+ */
+ static class GaugeObjectResource extends BaseMetricObject {
+ private final Gauge gauge;
+
+ /**
+ * Constructs a new Gauge resource object.
+ *
+ * @param newName name to use for the Gauge object
+ * @param newGauge Metrics Gauge object
+ */
+ public GaugeObjectResource(final String newName,
+ final Gauge newGauge) {
+ super(newName);
+ gauge = newGauge;
+ }
+
+ /**
+ * Gets the Metrics Gauge object for this resource.
+ *
+ * @return Metrics Gauge object.
+ */
+ public Gauge getGauge() {
+ return gauge;
+ }
+ }
+
+ /**
+ * Metric object that represents a Counter.
+ */
+ static class CounterObjectResource extends BaseMetricObject {
+ private final Counter counter;
+
+ /**
+ * Constructs a new Counter resource object.
+ *
+ * @param newName name to use for the Counter object
+ * @param newCounter Metrics Counter object
+ */
+ public CounterObjectResource(final String newName,
+ final Counter newCounter) {
+ super(newName);
+ counter = newCounter;
+ }
+
+ /**
+ * Gets the Metrics Counter object for this resource.
+ *
+ * @return Metrics Counter object.
+ */
+ public Counter getCounter() {
+ return counter;
+ }
+ }
+
+ /**
+ * Metric object that represents a Meter.
+ */
+ static class MeterObjectResource extends BaseMetricObject {
+ private final Meter meter;
+
+ /**
+ * Constructs a new Meter resource object.
+ *
+ * @param newName name to use for the Meter object
+ * @param newMeter Metrics Meter object
+ */
+ public MeterObjectResource(final String newName,
+ final Meter newMeter) {
+ super(newName);
+ meter = newMeter;
+ }
+
+ /**
+ * Gets the Metrics Meter object for this resource.
+ *
+ * @return Metrics Meter object.
+ */
+ public Meter getMeter() {
+ return meter;
+ }
+ }
+
+ /**
+ * Metric objerct that represents a Histogram.
+ */
+ static class HistogramObjectResource extends BaseMetricObject {
+ private final Histogram histogram;
+
+ /**
+ * Constructs a new Histogram resource object.
+ *
+ * @param newName name to use for Histogram object.
+ * @param newHistogram Metrics Histogram object.
+ */
+ public HistogramObjectResource(final String newName,
+ final Histogram newHistogram) {
+ super(newName);
+ histogram = newHistogram;
+ }
+
+ /**
+ * Gets the Metrics Histogram object for this resource.
+ *
+ * @return Metrics Histogram Object
+ */
+ public Histogram getHistogram() {
+ return histogram;
+ }
+ }
+
+
+ private List<TimerObjectResource> timers;
+ private List<GaugeObjectResource> gauges;
+ private List<CounterObjectResource> counters;
+ private List<MeterObjectResource> meters;
+ private List<HistogramObjectResource> histograms;
+
+ /**
+ * Gets the list of Gauge objects.
+ *
+ * @return list of gauges
+ */
+ public List<GaugeObjectResource> getGauges() {
+ return gauges;
+ }
+
+ /**
+ * Defines the list of Gauge objects.
+ *
+ * @param gauges list of gauges
+ */
+ public void setGauges(List<GaugeObjectResource> gauges) {
+ this.gauges = gauges;
+ }
+
+ /**
+ * Gets the list of Timer objects.
+ *
+ * @return list of Timers
+ */
+ public List<TimerObjectResource> getTimers() {
+ return timers;
+ }
+
+ /**
+ * Defines the list of Timer objects.
+ *
+ * @param newTimers list of Timers
+ */
+ public void setTimers(List<TimerObjectResource> newTimers) {
+ timers = newTimers;
+ }
+
+ /**
+ * Gets the list of Counter objects.
+ *
+ * @return list of Counters
+ */
+ public List<CounterObjectResource> getCounters() {
+ return counters;
+ }
+
+ /**
+ * Defines the list of Counter objects.
+ *
+ * @param counters list of Counters
+ */
+ public void setCounters(List<CounterObjectResource> counters) {
+ this.counters = counters;
+ }
+
+ /**
+ * Gets the list of Meter objects.
+ *
+ * @return list of Meters
+ */
+ public List<MeterObjectResource> getMeters() {
+ return meters;
+ }
+
+ /**
+ * Defines the list of Meter objects.
+ *
+ * @param meters list of Meters
+ */
+ public void setMeters(List<MeterObjectResource> meters) {
+ this.meters = meters;
+ }
+
+ /**
+ * Gets the list of Histogram objects.
+ *
+ * @return list of Histograms
+ */
+ public List<HistogramObjectResource> getHistograms() {
+ return histograms;
+ }
+
+ /**
+ * Defines the list of Histogram objects.
+ *
+ * @param histograms list of Histograms.
+ */
+ public void setHistograms(List<HistogramObjectResource> histograms) {
+ this.histograms = histograms;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/metrics/MetricsObjectSerializer.java b/src/main/java/net/onrc/onos/core/metrics/MetricsObjectSerializer.java
new file mode 100644
index 0000000..ba32381
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/MetricsObjectSerializer.java
@@ -0,0 +1,123 @@
+package net.onrc.onos.core.metrics;
+
+import com.codahale.metrics.json.MetricsModule;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.ser.std.SerializerBase;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * JSON serializer for the Metrics resource.
+ */
+public class MetricsObjectSerializer extends SerializerBase<MetricsObjectResource> {
+
+ /**
+ * Public constructor - just calls its super class constructor.
+ */
+ public MetricsObjectSerializer() {
+ super(MetricsObjectResource.class);
+ }
+
+ /**
+ * Convenience method to serialize a Metrics field.
+ *
+ * @param jsonGenerator generator to use for serialization
+ * @param fieldName name of the top level field
+ * @param serializedObjectJSON JSON representation from the Metrics serializer
+ * @param object internal resource for the Metric
+ * @throws IOException if JSON generation fails.
+ */
+ private void serializeItem(final JsonGenerator jsonGenerator,
+ final String fieldName,
+ final String serializedObjectJSON,
+ final MetricsObjectResource.BaseMetricObject object)
+ throws IOException {
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeStringField("name", object.getName());
+
+ // If you write the JSON for the Metric using a StringField, the
+ // generator applies an extra set of double quotes and breaks the
+ // syntax. You have to use the raw JSON output to get it right.
+ jsonGenerator.writeRaw(",\"" + fieldName + "\": " + serializedObjectJSON);
+ jsonGenerator.writeEndObject();
+ }
+
+ /**
+ * Serialize a MetricsObjectResource into JSON. For each kind of Metric,
+ * his serializes common ONOS defined fields like name and
+ * then calls the Metrics serializer to make the JSON string
+ * for the actual Metric.
+ *
+ * @param metrics resource for all ONOS Metrics
+ * @param jsonGenerator generator to use for the JSON output
+ * @param serializerProvider unused, needed for Override
+ * @throws IOException if any of the JSON serializations fail
+ */
+ @Override
+ @SuppressWarnings("rawtypes")
+ public void serialize(final MetricsObjectResource metrics,
+ final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider)
+ throws IOException {
+
+ final ObjectMapper mapper = new ObjectMapper().registerModule(
+ new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, false));
+ jsonGenerator.writeStartObject();
+
+ // serialize Timers
+ jsonGenerator.writeArrayFieldStart("timers");
+
+ for (final MetricsObjectResource.TimerObjectResource timer :
+ metrics.getTimers()) {
+ final String timerJSON = mapper.writeValueAsString(timer.getTimer());
+ serializeItem(jsonGenerator, "timer", timerJSON, timer);
+ }
+ jsonGenerator.writeEndArray();
+
+ // Serialize Gauges
+ jsonGenerator.writeArrayFieldStart("gauges");
+
+ for (final MetricsObjectResource.GaugeObjectResource gauge :
+ metrics.getGauges()) {
+ final String gaugeJSON = mapper.writeValueAsString(gauge.getGauge());
+ serializeItem(jsonGenerator, "gauge", gaugeJSON, gauge);
+ }
+ jsonGenerator.writeEndArray();
+
+ // Serialize Counters
+ jsonGenerator.writeArrayFieldStart("counters");
+
+ for (final MetricsObjectResource.CounterObjectResource counter :
+ metrics.getCounters()) {
+ final String counterJSON = mapper.writeValueAsString(counter.getCounter());
+ serializeItem(jsonGenerator, "counter", counterJSON, counter);
+ }
+ jsonGenerator.writeEndArray();
+
+ // Serialize Meters
+ jsonGenerator.writeArrayFieldStart("meters");
+
+ for (final MetricsObjectResource.MeterObjectResource meter :
+ metrics.getMeters()) {
+ final String meterJSON = mapper.writeValueAsString(meter.getMeter());
+ serializeItem(jsonGenerator, "meter", meterJSON, meter);
+ }
+ jsonGenerator.writeEndArray();
+
+ // Serialize Histograms
+ jsonGenerator.writeArrayFieldStart("histograms");
+
+ for (final MetricsObjectResource.HistogramObjectResource histogram :
+ metrics.getHistograms()) {
+ final String histogramJSON = mapper.writeValueAsString(histogram.getHistogram());
+ serializeItem(jsonGenerator, "histogram", histogramJSON, histogram);
+ }
+ jsonGenerator.writeEndArray();
+
+ jsonGenerator.writeEndObject();
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/core/metrics/MetricsResource.java b/src/main/java/net/onrc/onos/core/metrics/MetricsResource.java
new file mode 100644
index 0000000..64b7996
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/MetricsResource.java
@@ -0,0 +1,82 @@
+package net.onrc.onos.core.metrics;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Histogram;
+import org.restlet.representation.Representation;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * REST APIs for Metrics objects.
+ */
+public class MetricsResource extends ServerResource {
+
+ /**
+ * REST API to get all of the system's metrics.
+ *
+ * @return a Representation object containing the metrics
+ */
+ @Get("json")
+ @SuppressWarnings("rawtypes")
+ public Representation retrieve() throws Exception {
+ final MetricRegistry registry = OnosMetrics.getMetricsRegistry();
+ final MetricsObjectResource result = new MetricsObjectResource();
+
+ final List<MetricsObjectResource.TimerObjectResource> timers =
+ new ArrayList<>();
+ final List<MetricsObjectResource.GaugeObjectResource> gauges =
+ new ArrayList<>();
+ final List<MetricsObjectResource.CounterObjectResource> counters =
+ new ArrayList<>();
+ final List<MetricsObjectResource.MeterObjectResource> meters =
+ new ArrayList<>();
+ final List<MetricsObjectResource.HistogramObjectResource> histograms =
+ new ArrayList<>();
+
+ for (final Map.Entry<String, Timer> timer :
+ registry.getTimers().entrySet()) {
+ timers.add(new MetricsObjectResource.TimerObjectResource(
+ timer.getKey(), timer.getValue()));
+ }
+ result.setTimers(timers);
+
+ for (final Map.Entry<String, Gauge> gauge :
+ registry.getGauges().entrySet()) {
+ gauges.add(new MetricsObjectResource.GaugeObjectResource(
+ gauge.getKey(), gauge.getValue()));
+ }
+ result.setGauges(gauges);
+
+ for (final Map.Entry<String, Counter> counter :
+ registry.getCounters().entrySet()) {
+ counters.add(new MetricsObjectResource.CounterObjectResource(
+ counter.getKey(), counter.getValue()));
+ }
+ result.setCounters(counters);
+
+ for (final Map.Entry<String, Meter> meter :
+ registry.getMeters().entrySet()) {
+ meters.add(new MetricsObjectResource.MeterObjectResource(
+ meter.getKey(), meter.getValue()));
+ }
+ result.setMeters(meters);
+
+ for (final Map.Entry<String, Histogram> histogram :
+ registry.getHistograms().entrySet()) {
+ histograms.add(new MetricsObjectResource.HistogramObjectResource(
+ histogram.getKey(), histogram.getValue()));
+ }
+ result.setHistograms(histograms);
+
+ return toRepresentation(result, null);
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/core/metrics/MetricsWebRoutable.java b/src/main/java/net/onrc/onos/core/metrics/MetricsWebRoutable.java
new file mode 100644
index 0000000..2761e71
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/MetricsWebRoutable.java
@@ -0,0 +1,31 @@
+package net.onrc.onos.core.metrics;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+/**
+ * Restlet Router for Metrics REST APIs.
+ */
+public class MetricsWebRoutable implements RestletRoutable {
+ /**
+ * Creates the Restlet router and binds to the proper resources.
+ */
+ @Override
+ public Restlet getRestlet(Context context) {
+ Router router = new Router(context);
+ // GET all metrics
+ router.attach("", MetricsResource.class);
+ return router;
+ }
+
+
+ /**
+ * Sets the base path for the Metrics.
+ */
+ @Override
+ public String basePath() {
+ return "/wm/onos/metrics";
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/metrics/OnosMetrics.java b/src/main/java/net/onrc/onos/core/metrics/OnosMetrics.java
new file mode 100644
index 0000000..bfd603f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/OnosMetrics.java
@@ -0,0 +1,28 @@
+package net.onrc.onos.core.metrics;
+
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * This class acts a singleton to hold the Metrics registry for ONOS.
+ */
+public final class OnosMetrics {
+
+ /**
+ * Hide constructor. The only way to get the registry is through the
+ * singleton getter.
+ */
+ private OnosMetrics() {}
+
+ private static final MetricRegistry METRICS_REGISTRY = new MetricRegistry();
+
+ /**
+ * Get the singleton Metrics registry. A single instance of
+ * the registry is statically allocated and then used by all callers.
+ *
+ * @return Metrics registry
+ */
+ public static MetricRegistry getMetricsRegistry() {
+ return METRICS_REGISTRY;
+ }
+}
+
diff --git a/src/main/java/net/onrc/onos/core/metrics/OnosMetricsModule.java b/src/main/java/net/onrc/onos/core/metrics/OnosMetricsModule.java
new file mode 100644
index 0000000..6f046cf
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/metrics/OnosMetricsModule.java
@@ -0,0 +1,53 @@
+package net.onrc.onos.core.metrics;
+
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.onrc.onos.core.registry.IControllerRegistryService;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Floodlight module to add the REST APIs for Metrics.
+ */
+public class OnosMetricsModule implements IFloodlightModule {
+
+ private IRestApiService restApi;
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ return null;
+ }
+
+ @Override
+ public Map<Class<? extends IFloodlightService>, IFloodlightService>
+ getServiceImpls() {
+ return null;
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+ List<Class<? extends IFloodlightService>> dependencies =
+ new ArrayList<>();
+ dependencies.add(IControllerRegistryService.class);
+ dependencies.add(IRestApiService.class);
+ return dependencies;
+ }
+
+ @Override
+ public void init(FloodlightModuleContext context)
+ throws FloodlightModuleException {
+ restApi = context.getServiceImpl(IRestApiService.class);
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+ restApi.addRestletRoutable(new MetricsWebRoutable());
+ }
+}
+