Jian Li
Committed by Gerrit Code Review

[Falcon][ONOS-3601] Add REST API for metrics service with unit test

Change-Id: I33ec561d1d83c6f1167e960bc2f684a117e6ea9c
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.codec.impl; 16 package org.onosproject.codec.impl;
17 17
18 +import com.codahale.metrics.Metric;
18 import com.google.common.collect.ImmutableSet; 19 import com.google.common.collect.ImmutableSet;
19 20
20 import org.apache.felix.scr.annotations.Activate; 21 import org.apache.felix.scr.annotations.Activate;
...@@ -104,6 +105,7 @@ public class CodecManager implements CodecService { ...@@ -104,6 +105,7 @@ public class CodecManager implements CodecService {
104 registerCodec(Load.class, new LoadCodec()); 105 registerCodec(Load.class, new LoadCodec());
105 registerCodec(TableStatisticsEntry.class, new TableStatisticsEntryCodec()); 106 registerCodec(TableStatisticsEntry.class, new TableStatisticsEntryCodec());
106 registerCodec(PortStatistics.class, new PortStatisticsCodec()); 107 registerCodec(PortStatistics.class, new PortStatisticsCodec());
108 + registerCodec(Metric.class, new MetricCodec());
107 log.info("Started"); 109 log.info("Started");
108 } 110 }
109 111
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.codec.impl;
17 +
18 +import com.codahale.metrics.Counter;
19 +import com.codahale.metrics.Gauge;
20 +import com.codahale.metrics.Meter;
21 +import com.codahale.metrics.Metric;
22 +import com.codahale.metrics.Histogram;
23 +import com.codahale.metrics.Timer;
24 +
25 +import com.fasterxml.jackson.databind.node.ObjectNode;
26 +import org.onosproject.codec.CodecContext;
27 +import org.onosproject.codec.JsonCodec;
28 +
29 +import static com.google.common.base.Preconditions.checkNotNull;
30 +
31 +/**
32 + * Codec for the Metric class.
33 + */
34 +public class MetricCodec extends JsonCodec<Metric> {
35 +
36 + // JSON field names
37 + private static final String COUNTER = "counter";
38 +
39 + private static final String GAUGE = "gauge";
40 + private static final String VALUE = "value";
41 +
42 + private static final String METER = "meter";
43 + private static final String MEAN_RATE = "mean_rate";
44 + private static final String ONE_MIN_RATE = "1_min_rate";
45 + private static final String FIVE_MIN_RATE = "5_min_rate";
46 + private static final String FIFT_MIN_RATE = "15_min_rate";
47 +
48 + private static final String HISTOGRAM = "histogram";
49 + private static final String MIN = "min";
50 + private static final String MAX = "max";
51 + private static final String MEAN = "mean";
52 + private static final String STDDEV = "stddev";
53 +
54 + private static final String TIMER = "timer";
55 +
56 + @Override
57 + public ObjectNode encode(Metric metric, CodecContext context) {
58 + checkNotNull(metric, "Metric cannot be null");
59 +
60 + ObjectNode objectNode = context.mapper().createObjectNode();
61 + ObjectNode dataNode = context.mapper().createObjectNode();
62 +
63 + if (metric instanceof Counter) {
64 + dataNode.put(COUNTER, ((Counter) metric).getCount());
65 + objectNode.set(COUNTER, dataNode);
66 + } else if (metric instanceof Gauge) {
67 + objectNode.put(VALUE, ((Gauge) metric).getValue().toString());
68 + objectNode.set(GAUGE, dataNode);
69 + } else if (metric instanceof Meter) {
70 + dataNode.put(COUNTER, ((Meter) metric).getCount());
71 + dataNode.put(MEAN_RATE, ((Meter) metric).getMeanRate());
72 + dataNode.put(ONE_MIN_RATE, ((Meter) metric).getOneMinuteRate());
73 + dataNode.put(FIVE_MIN_RATE, ((Meter) metric).getFiveMinuteRate());
74 + dataNode.put(FIFT_MIN_RATE, ((Meter) metric).getFifteenMinuteRate());
75 + objectNode.set(METER, dataNode);
76 + } else if (metric instanceof Histogram) {
77 + dataNode.put(COUNTER, ((Histogram) metric).getCount());
78 + dataNode.put(MEAN, ((Histogram) metric).getSnapshot().getMean());
79 + dataNode.put(MIN, ((Histogram) metric).getSnapshot().getMin());
80 + dataNode.put(MAX, ((Histogram) metric).getSnapshot().getMax());
81 + dataNode.put(STDDEV, ((Histogram) metric).getSnapshot().getStdDev());
82 + objectNode.set(HISTOGRAM, dataNode);
83 + } else if (metric instanceof Timer) {
84 + dataNode.put(COUNTER, ((Timer) metric).getCount());
85 + dataNode.put(MEAN_RATE, ((Timer) metric).getMeanRate());
86 + dataNode.put(ONE_MIN_RATE, ((Timer) metric).getOneMinuteRate());
87 + dataNode.put(FIVE_MIN_RATE, ((Timer) metric).getFiveMinuteRate());
88 + dataNode.put(FIFT_MIN_RATE, ((Timer) metric).getFifteenMinuteRate());
89 + dataNode.put(MEAN, nanoToMs(((Timer) metric).getSnapshot().getMean()));
90 + dataNode.put(MIN, nanoToMs(((Timer) metric).getSnapshot().getMin()));
91 + dataNode.put(MAX, nanoToMs(((Timer) metric).getSnapshot().getMax()));
92 + dataNode.put(STDDEV, nanoToMs(((Timer) metric).getSnapshot().getStdDev()));
93 + objectNode.set(TIMER, dataNode);
94 + }
95 + return objectNode;
96 + }
97 +
98 + private double nanoToMs(double nano) {
99 + return nano / 1_000_000D;
100 + }
101 +}
...@@ -102,7 +102,9 @@ ...@@ -102,7 +102,9 @@
102 org.onlab.osgi.*, 102 org.onlab.osgi.*,
103 org.onlab.packet.*, 103 org.onlab.packet.*,
104 org.onlab.rest.*, 104 org.onlab.rest.*,
105 - org.onosproject.* 105 + org.onosproject.*,
106 + org.onlab.metrics.*,
107 + com.codahale.metrics.*
106 </Import-Package> 108 </Import-Package>
107 <Web-ContextPath>${web.context}</Web-ContextPath> 109 <Web-ContextPath>${web.context}</Web-ContextPath>
108 </instructions> 110 </instructions>
......
...@@ -40,7 +40,8 @@ public class CoreWebApplication extends AbstractWebApplication { ...@@ -40,7 +40,8 @@ public class CoreWebApplication extends AbstractWebApplication {
40 TopologyWebResource.class, 40 TopologyWebResource.class,
41 ConfigWebResource.class, 41 ConfigWebResource.class,
42 PathsWebResource.class, 42 PathsWebResource.class,
43 - StatisticsWebResource.class 43 + StatisticsWebResource.class,
44 + MetricsWebResource.class
44 ); 45 );
45 } 46 }
46 } 47 }
......
1 +/*
2 + * Copyright 2014-2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.rest.resources;
17 +
18 +import com.codahale.metrics.Counter;
19 +import com.codahale.metrics.Gauge;
20 +import com.codahale.metrics.Metric;
21 +import com.codahale.metrics.Histogram;
22 +import com.codahale.metrics.Meter;
23 +import com.codahale.metrics.Timer;
24 +import com.codahale.metrics.MetricFilter;
25 +import com.fasterxml.jackson.databind.node.ArrayNode;
26 +import com.fasterxml.jackson.databind.node.ObjectNode;
27 +import com.google.common.collect.Ordering;
28 +import com.google.common.collect.TreeMultimap;
29 +import org.onlab.metrics.MetricsService;
30 +import org.onosproject.rest.AbstractWebResource;
31 +
32 +import javax.ws.rs.GET;
33 +import javax.ws.rs.Path;
34 +import javax.ws.rs.PathParam;
35 +import javax.ws.rs.Produces;
36 +import javax.ws.rs.core.MediaType;
37 +import javax.ws.rs.core.Response;
38 +import java.util.Comparator;
39 +import java.util.Map;
40 +
41 +/**
42 + * Query metrics.
43 + */
44 +@Path("metrics")
45 +public class MetricsWebResource extends AbstractWebResource {
46 +
47 + final MetricsService service = get(MetricsService.class);
48 + final ObjectNode root = mapper().createObjectNode();
49 +
50 + /**
51 + * Get stats information of all metrics. Returns array of all information for
52 + * all metrics.
53 + *
54 + * @return metric information as array
55 + * @onos.rsModel Metrics
56 + */
57 + @GET
58 + @Produces(MediaType.APPLICATION_JSON)
59 + public Response getAllMetrics() {
60 + ArrayNode metricsNode = root.putArray("metrics");
61 + service.getMetrics().forEach((name, metric) -> {
62 + ObjectNode item = mapper().createObjectNode();
63 + item.put("name", name);
64 + item.set("metric", codec(Metric.class).encode(metric, this));
65 + metricsNode.add(item);
66 + });
67 +
68 + return ok(root).build();
69 + }
70 +
71 + /**
72 + * Get stats information of a metric. Returns array of all information for the
73 + * specified metric.
74 + *
75 + * @param metricName metric name
76 + * @return metric information as array
77 + * @onos.rsModel Metric
78 + */
79 + @GET
80 + @Produces(MediaType.APPLICATION_JSON)
81 + @Path("{metricName}")
82 + public Response getMetricByName(@PathParam("metricName") String metricName) {
83 + ObjectNode metricNode = root.putObject("metric");
84 + MetricFilter filter = metricName != null ? (name, metric) -> name.equals(metricName) : MetricFilter.ALL;
85 + TreeMultimap<String, Metric> matched = listMetrics(service, filter);
86 +
87 + matched.asMap().get(metricName).forEach(m -> {
88 + metricNode.set(metricName, codec(Metric.class).encode(m, this));
89 + });
90 +
91 + return ok(root).build();
92 + }
93 +
94 + private TreeMultimap<String, Metric> listMetrics(MetricsService metricsService, MetricFilter filter) {
95 + TreeMultimap<String, Metric> metrics = TreeMultimap.create(Comparator.naturalOrder(), Ordering.arbitrary());
96 +
97 + Map<String, Counter> counters = metricsService.getCounters(filter);
98 + for (Map.Entry<String, Counter> entry : counters.entrySet()) {
99 + metrics.put(entry.getKey(), entry.getValue());
100 + }
101 + Map<String, Gauge> gauges = metricsService.getGauges(filter);
102 + for (Map.Entry<String, Gauge> entry : gauges.entrySet()) {
103 + metrics.put(entry.getKey(), entry.getValue());
104 + }
105 + Map<String, Histogram> histograms = metricsService.getHistograms(filter);
106 + for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {
107 + metrics.put(entry.getKey(), entry.getValue());
108 + }
109 + Map<String, Meter> meters = metricsService.getMeters(filter);
110 + for (Map.Entry<String, Meter> entry : meters.entrySet()) {
111 + metrics.put(entry.getKey(), entry.getValue());
112 + }
113 + Map<String, Timer> timers = metricsService.getTimers(filter);
114 + for (Map.Entry<String, Timer> entry : timers.entrySet()) {
115 + metrics.put(entry.getKey(), entry.getValue());
116 + }
117 +
118 + return metrics;
119 + }
120 +}
1 +{
2 + "type": "object",
3 + "title": "metric",
4 + "required": [
5 + "name",
6 + "metric"
7 + ],
8 + "properties": {
9 + "name": {
10 + "type": "string",
11 + "example": "cpu"
12 + },
13 + "metric": {
14 + "type": "object",
15 + "title": "metric",
16 + "optional": [
17 + "counter",
18 + "gauge",
19 + "meter",
20 + "histogram",
21 + "timer"
22 + ],
23 + "properties": {
24 + "counter": {
25 + "type": "object",
26 + "required": [
27 + "counter"
28 + ],
29 + "properties": {
30 + "counter": {
31 + "type": "integer",
32 + "example": "1"
33 + }
34 + }
35 + },
36 + "gauge": {
37 + "type": "object",
38 + "required": [
39 + "value"
40 + ],
41 + "properties": {
42 + "value": "string",
43 + "example": "1"
44 + }
45 + },
46 + "meter": {
47 + "type": "object",
48 + "required": [
49 + "counter",
50 + "mean_rate",
51 + "1_min_rate",
52 + "5_min_rate",
53 + "15_min_rate"
54 + ],
55 + "properties": {
56 + "counter": {
57 + "type": "integer",
58 + "example": "1"
59 + },
60 + "mean_rate": {
61 + "type": "double",
62 + "example": "1.0"
63 + },
64 + "1_min_rate": {
65 + "type": "double",
66 + "example": "1.0"
67 + },
68 + "5_min_rate": {
69 + "type": "double",
70 + "example": "1.0"
71 + },
72 + "15_min_rate": {
73 + "type": "double",
74 + "example": "1.0"
75 + }
76 + }
77 + },
78 + "histogram": {
79 + "type": "object",
80 + "required": [
81 + "counter",
82 + "mean",
83 + "min",
84 + "max",
85 + "stddev"
86 + ],
87 + "properties": {
88 + "counter": {
89 + "type": "integer",
90 + "example": "1"
91 + },
92 + "mean": {
93 + "type": "double",
94 + "example": "1.0"
95 + },
96 + "min": {
97 + "type": "double",
98 + "example": "1.0"
99 + },
100 + "max": {
101 + "type": "double",
102 + "example": "1.0"
103 + },
104 + "stddev": {
105 + "type": "double",
106 + "example": "1.0"
107 + }
108 + }
109 + },
110 + "timer": {
111 + "type": "object",
112 + "required": [
113 + "counter",
114 + "mean_rate",
115 + "1_min_rate",
116 + "5_min_rate",
117 + "15_min_rate",
118 + "mean",
119 + "min",
120 + "max",
121 + "stddev"
122 + ],
123 + "properties": {
124 + "counter": {
125 + "type": "integer",
126 + "example": "1"
127 + },
128 + "mean_rate": {
129 + "type": "double",
130 + "example": "1.0"
131 + },
132 + "1_min_rate": {
133 + "type": "double",
134 + "example": "1.0"
135 + },
136 + "5_min_rate": {
137 + "type": "double",
138 + "example": "1.0"
139 + },
140 + "15_min_rate": {
141 + "type": "double",
142 + "example": "1.0"
143 + },
144 + "mean": {
145 + "type": "double",
146 + "example": "1.0"
147 + },
148 + "min": {
149 + "type": "double",
150 + "example": "1.0"
151 + },
152 + "max": {
153 + "type": "double",
154 + "example": "1.0"
155 + },
156 + "stddev": {
157 + "type": "double",
158 + "example": "1.0"
159 + }
160 + }
161 + }
162 + }
163 + }
164 + }
165 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "type": "object",
3 + "title": "metrics",
4 + "required": [
5 + "metrics"
6 + ],
7 + "properties": {
8 + "metrics": {
9 + "type": "array",
10 + "xml": {
11 + "name": "metrics",
12 + "wrapped": true
13 + },
14 + "items": {
15 + "type": "object",
16 + "title": "metric",
17 + "required": [
18 + "name",
19 + "metric"
20 + ],
21 + "properties": {
22 + "name": {
23 + "type": "string",
24 + "example": "cpu"
25 + },
26 + "metric": {
27 + "type": "object",
28 + "optional": [
29 + "counter",
30 + "gauge",
31 + "meter",
32 + "histogram",
33 + "timer"
34 + ],
35 + "properties": {
36 + "counter": {
37 + "type": "object",
38 + "required": [
39 + "counter"
40 + ],
41 + "properties": {
42 + "counter": {
43 + "type": "integer",
44 + "example": "1"
45 + }
46 + }
47 + },
48 + "gauge": {
49 + "type": "object",
50 + "required": [
51 + "value"
52 + ],
53 + "properties": {
54 + "value": "string",
55 + "example": "1"
56 + }
57 + },
58 + "meter": {
59 + "type": "object",
60 + "required": [
61 + "counter",
62 + "mean_rate",
63 + "1_min_rate",
64 + "5_min_rate",
65 + "15_min_rate"
66 + ],
67 + "properties": {
68 + "counter": {
69 + "type": "integer",
70 + "example": "1"
71 + },
72 + "mean_rate": {
73 + "type": "double",
74 + "example": "1.0"
75 + },
76 + "1_min_rate": {
77 + "type": "double",
78 + "example": "1.0"
79 + },
80 + "5_min_rate": {
81 + "type": "double",
82 + "example": "1.0"
83 + },
84 + "15_min_rate": {
85 + "type": "double",
86 + "example": "1.0"
87 + }
88 + }
89 + },
90 + "histogram": {
91 + "type": "object",
92 + "required": [
93 + "counter",
94 + "mean",
95 + "min",
96 + "max",
97 + "stddev"
98 + ],
99 + "properties": {
100 + "counter": {
101 + "type": "integer",
102 + "example": "1"
103 + },
104 + "mean": {
105 + "type": "double",
106 + "example": "1.0"
107 + },
108 + "min": {
109 + "type": "double",
110 + "example": "1.0"
111 + },
112 + "max": {
113 + "type": "double",
114 + "example": "1.0"
115 + },
116 + "stddev": {
117 + "type": "double",
118 + "example": "1.0"
119 + }
120 + }
121 + },
122 + "timer": {
123 + "type": "object",
124 + "required": [
125 + "counter",
126 + "mean_rate",
127 + "1_min_rate",
128 + "5_min_rate",
129 + "15_min_rate",
130 + "mean",
131 + "min",
132 + "max",
133 + "stddev"
134 + ],
135 + "properties": {
136 + "counter": {
137 + "type": "integer",
138 + "example": "1"
139 + },
140 + "mean_rate": {
141 + "type": "double",
142 + "example": "1.0"
143 + },
144 + "1_min_rate": {
145 + "type": "double",
146 + "example": "1.0"
147 + },
148 + "5_min_rate": {
149 + "type": "double",
150 + "example": "1.0"
151 + },
152 + "15_min_rate": {
153 + "type": "double",
154 + "example": "1.0"
155 + },
156 + "mean": {
157 + "type": "double",
158 + "example": "1.0"
159 + },
160 + "min": {
161 + "type": "double",
162 + "example": "1.0"
163 + },
164 + "max": {
165 + "type": "double",
166 + "example": "1.0"
167 + },
168 + "stddev": {
169 + "type": "double",
170 + "example": "1.0"
171 + }
172 + }
173 + }
174 + }
175 + }
176 + }
177 + }
178 + }
179 + }
180 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2014-2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.rest;
17 +
18 +import com.codahale.metrics.Counter;
19 +import com.codahale.metrics.Meter;
20 +import com.codahale.metrics.Metric;
21 +import com.eclipsesource.json.JsonArray;
22 +import com.eclipsesource.json.JsonObject;
23 +import com.google.common.collect.ImmutableMap;
24 +import com.sun.jersey.api.client.WebResource;
25 +import org.junit.After;
26 +import org.junit.Before;
27 +import org.junit.Test;
28 +import org.onlab.metrics.MetricsService;
29 +import org.onlab.osgi.ServiceDirectory;
30 +import org.onlab.osgi.TestServiceDirectory;
31 +import org.onlab.rest.BaseResource;
32 +import org.onosproject.codec.CodecService;
33 +import org.onosproject.codec.impl.CodecManager;
34 +
35 +import static org.easymock.EasyMock.createMock;
36 +import static org.easymock.EasyMock.expect;
37 +import static org.easymock.EasyMock.replay;
38 +import static org.easymock.EasyMock.verify;
39 +import static org.hamcrest.Matchers.containsString;
40 +import static org.hamcrest.Matchers.is;
41 +import static org.hamcrest.Matchers.notNullValue;
42 +import static org.junit.Assert.assertThat;
43 +
44 +/**
45 + * Unit tests for Metrics REST APIs.
46 + */
47 +public class MetricsResourceTest extends ResourceTest {
48 + MetricsService mockMetricsService;
49 +
50 + /**
51 + * Initializes test mocks and environment.
52 + */
53 + @Before
54 + public void setUpTest() {
55 + mockMetricsService = createMock(MetricsService.class);
56 +
57 + // Register the services needed for the test
58 + final CodecManager codecService = new CodecManager();
59 + codecService.activate();
60 + ServiceDirectory testDirectory =
61 + new TestServiceDirectory()
62 + .add(MetricsService.class, mockMetricsService)
63 + .add(CodecService.class, codecService);
64 + BaseResource.setServiceDirectory(testDirectory);
65 + }
66 +
67 + /**
68 + * Verifies mocks.
69 + */
70 + @After
71 + public void tearDownTest() {
72 + verify(mockMetricsService);
73 + }
74 +
75 + /**
76 + * Tests that a fetch of a non-existent object throws an exception.
77 + */
78 + @Test
79 + public void testBadGet() {
80 + Counter onosCounter = new Counter();
81 + onosCounter.inc();
82 +
83 + Meter onosMeter = new Meter();
84 + onosMeter.mark();
85 +
86 + ImmutableMap<String, Metric> metrics =
87 + new ImmutableMap.Builder<String, Metric>()
88 + .put("onosCounter", onosCounter)
89 + .put("onosMeter", onosMeter)
90 + .build();
91 +
92 + expect(mockMetricsService.getMetrics())
93 + .andReturn(metrics)
94 + .anyTimes();
95 +
96 + replay(mockMetricsService);
97 +
98 + WebResource rs = resource();
99 + String response = rs.path("metrics").get(String.class);
100 + assertThat(response, containsString("{\"metrics\":["));
101 +
102 + JsonObject result = JsonObject.readFrom(response);
103 + assertThat(result, notNullValue());
104 +
105 + JsonArray jsonMetrics = result.get("metrics").asArray();
106 + assertThat(jsonMetrics, notNullValue());
107 + assertThat(jsonMetrics.size(), is(2));
108 + }
109 +}