Added performance-related metrics for the Topology and Intents:

===

 * "Topology.EventNotification.LastEventTimestamp"
   Timestamp of the last Topology event (system nanoseconds)

 * "Topology.EventNotification.ListenerEventRate"
   Rate of the Topology events published to the Topology listeners

===

 * "Intents.AddOperation.BeginOperationTimestamp"
   Timestamp of the incoming Add Intent API operation (system nanoseconds)

 * "Intents.AddOperation.EndOperationTimestamp"
   Timestamp of the Add Intent operation completion (system nanoseconds)

 * "Intents.AddOperation.IncomingRate"
   Rate of the incoming Add Intent API operations

 * "Intents.AddOperation.ProcessingRate"
   Rate of processing the Add Intent operations

===

 * "Intents.RemoveOperation.BeginOperationTimestamp"
   Timestamp of the incoming Remove Intent API operation (system nanoseconds)

 * "Intents.RemoveOperation.EndOperationTimestamp"
   Timestamp of the Remove Intent operation completion (system nanoseconds)

 * "Intents.RemoveOperation.IncomingRate"
   Rate of the incoming Remove Intent API operations

 * "Intents.RemoveOperation.ProcessingRate"
   Rate of processing the Remove Intent operations

===

All performance metrics are exposed via the Metrics REST API:

 * GET all metrics:
url = "http://%s:%s/wm/onos/metrics" % (self.onos_ip, self.onos_port)

 * GET a specific metric:
url = "http://%s:%s/wm/onos/metrics?ids=%s" % (self.onos_ip, self.onos_port, args.metric_id)

   where "metric_id" is the name of the Metric. E.g.:
   ids=Topology.EventNotification.LastEventTimestamp

 * GET multiple metrics:
url = "http://%s:%s/wm/onos/metrics?ids=%s" % (self.onos_ip, self.onos_port, args.metric_id)

   where "metric_id" is comma-separated list of Metric names. E.g:
   ids=Topology.EventNotification.LastEventTimestamp,Topology.EventNotification.ListenerEventRate

===

The JSON format of the output is the following:

{
    "meters": [
        {
            "name": "Intents.AddOperation.IncomingRate",
            "meter": {
                "count": 2,
                "mean_rate": 0.007488255279864508,
                "m5_rate": 0.006082798637345469,
                "m15_rate": 0.021083988195124116,
                "units": "events/second",
                "m1_rate": 0.002155350653737004
            }
        },
        ...
    ],
    "histograms": [],
    "timers": [],
    "gauges": [
        {
            "gauge": {
                "value": 179956769775795
            },
            "name": "Intents.AddOperation.BeginOperationTimestamp"
        },
        ...
    ],
    "counters": []
}

where
  - "meter.count" is the number of events
  - "meter.mean_rate" is the mean rate of the events
  - "meter.m5_rate" is the rate of the events over the last 5-minute interval
  - "meter.m15_rate" is the rate of the events over the last 15-minute interval
  - "meter.m1_rate" is the rate of the events over the last 1-minute interval
  - "meter.units" is the units of the rate (should be "events/second")

  - "gauge.value" is the value of the particular metric.
     In case of the "*Timestamp" metrics, it is the system nanoseconds
     timestamp for the particular event or operation.

Also, fixed some of the unit tests.
NOTE: Currently, for some of the tests we have to explicitly
remove all metrics when tearing-down a test.
In the future we should have a single base unit test class,
and only its tearDown() method should do such cleanup.

Change-Id: Iad5b47b908a29dcfd9fb08e7a010ddf2627fd808
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyManager.java b/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
index 7e7f2b8..aeb06c2 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
@@ -26,6 +26,9 @@
 import net.onrc.onos.core.datastore.topology.KVLink;
 import net.onrc.onos.core.datastore.topology.KVPort;
 import net.onrc.onos.core.datastore.topology.KVSwitch;
+import net.onrc.onos.core.metrics.OnosMetrics;
+import net.onrc.onos.core.metrics.OnosMetrics.MetricsComponent;
+import net.onrc.onos.core.metrics.OnosMetrics.MetricsFeature;
 import net.onrc.onos.core.registry.IControllerRegistryService;
 import net.onrc.onos.core.util.Dpid;
 import net.onrc.onos.core.util.EventEntry;
@@ -36,6 +39,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Meter;
 import com.esotericsoftware.kryo.Kryo;
 
 /**
@@ -69,6 +74,32 @@
     private Kryo kryo = KryoFactory.newKryoObject();
 
     //
+    // Metrics
+    //
+    private static final MetricsComponent METRICS_COMPONENT =
+        OnosMetrics.registerComponent("Topology");
+    private static final MetricsFeature METRICS_FEATURE_EVENT_NOTIFICATION =
+        METRICS_COMPONENT.registerFeature("EventNotification");
+    //
+    // Timestamp of the last Topology event (system nanoseconds)
+    private volatile long lastEventTimestamp = 0;
+    private final Gauge<Long> gaugeLastEventTimestamp =
+        OnosMetrics.registerMetric(METRICS_COMPONENT,
+                                   METRICS_FEATURE_EVENT_NOTIFICATION,
+                                   "LastEventTimestamp",
+                                   new Gauge<Long>() {
+                                       @Override
+                                       public Long getValue() {
+                                           return lastEventTimestamp;
+                                       }
+                                   });
+    // Rate of the Topology events published to the Topology listeners
+    private final Meter listenerEventRate =
+        OnosMetrics.createMeter(METRICS_COMPONENT,
+                                METRICS_FEATURE_EVENT_NOTIFICATION,
+                                "ListenerEventRate");
+
+    //
     // Local state for keeping track of reordered events.
     // NOTE: Switch Events are not affected by the event reordering.
     //
@@ -466,11 +497,23 @@
             }
         }
 
+        //
+        // Update the metrics
+        //
+        long totalEvents =
+            apiAddedSwitchEvents.size() + apiRemovedSwitchEvents.size() +
+            apiAddedPortEvents.size() + apiRemovedPortEvents.size() +
+            apiAddedLinkEvents.size() + apiRemovedLinkEvents.size() +
+            apiAddedHostEvents.size() + apiRemovedHostEvents.size();
+        this.listenerEventRate.mark(totalEvents);
+        this.lastEventTimestamp = System.nanoTime();
+
+        //
         // Deliver the events
-        long timestamp = System.nanoTime();
+        //
         for (ITopologyListener listener : this.topologyListeners) {
             TopologyEvents events =
-                new TopologyEvents(timestamp,
+                new TopologyEvents(lastEventTimestamp,
                                    kryo.copy(apiAddedSwitchEvents),
                                    kryo.copy(apiRemovedSwitchEvents),
                                    kryo.copy(apiAddedPortEvents),