Jian Li
Committed by Gerrit Code Review

[ONOS-3663] Implement control metrics REST API

Change-Id: Ifc901863e55cdd161d704ecd584242786671af87
1 +/*
2 + * Copyright 2016 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.cpman.codec;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onosproject.codec.CodecContext;
20 +import org.onosproject.codec.JsonCodec;
21 +import org.onosproject.cpman.ControlLoad;
22 +
23 +/**
24 + * Control load codec.
25 + */
26 +public final class ControlLoadCodec extends JsonCodec<ControlLoad> {
27 +
28 + private static final String TIME = "time";
29 + private static final String LATEST = "latest";
30 + private static final String AVERAGE = "average";
31 +
32 + @Override
33 + public ObjectNode encode(ControlLoad controlLoad, CodecContext context) {
34 + return context.mapper().createObjectNode()
35 + .put(TIME, controlLoad.time())
36 + .put(LATEST, controlLoad.latest())
37 + .put(AVERAGE, controlLoad.average());
38 + }
39 +}
1 +/*
2 + * Copyright 2016 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 +/**
17 + * Implementations of the codec broker and cpman entity JSON codecs.
18 + */
19 +package org.onosproject.cpman.codec;
1 +/*
2 + * Copyright 2016 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.cpman.rest;
17 +
18 +import org.apache.felix.scr.annotations.Activate;
19 +import org.apache.felix.scr.annotations.Component;
20 +import org.apache.felix.scr.annotations.Deactivate;
21 +import org.apache.felix.scr.annotations.Reference;
22 +import org.apache.felix.scr.annotations.ReferenceCardinality;
23 +import org.onosproject.codec.CodecService;
24 +import org.onosproject.cpman.ControlLoad;
25 +import org.onosproject.cpman.codec.ControlLoadCodec;
26 +import org.slf4j.Logger;
27 +
28 +import static org.slf4j.LoggerFactory.getLogger;
29 +
30 +/**
31 + * Implementation of the JSON codec brokering service for CPMan app.
32 + */
33 +@Component(immediate = true)
34 +public class CPManCodecRegistrator {
35 +
36 + private final Logger log = getLogger(getClass());
37 +
38 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
39 + protected CodecService codecService;
40 +
41 + @Activate
42 + public void activate() {
43 + codecService.registerCodec(ControlLoad.class, new ControlLoadCodec());
44 +
45 + log.info("Started");
46 + }
47 +
48 + @Deactivate
49 + public void deactivate() {
50 + log.info("Stopped");
51 + }
52 +}
...@@ -26,6 +26,7 @@ import java.util.Set; ...@@ -26,6 +26,7 @@ import java.util.Set;
26 public class CPManWebApplication extends AbstractWebApplication { 26 public class CPManWebApplication extends AbstractWebApplication {
27 @Override 27 @Override
28 public Set<Class<?>> getClasses() { 28 public Set<Class<?>> getClasses() {
29 - return getClasses(ControlMetricsCollectorWebResource.class); 29 + return getClasses(SystemMetricsCollectorWebResource.class,
30 + ControlMetricsWebResource.class);
30 } 31 }
31 } 32 }
......
1 +/*
2 + * Copyright 2016 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.cpman.rest;
17 +
18 +import com.fasterxml.jackson.databind.node.ArrayNode;
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +import org.onosproject.cluster.ClusterService;
21 +import org.onosproject.cluster.NodeId;
22 +import org.onosproject.cpman.ControlLoad;
23 +import org.onosproject.cpman.ControlMetricType;
24 +import org.onosproject.cpman.ControlPlaneMonitorService;
25 +import org.onosproject.net.DeviceId;
26 +import org.onosproject.rest.AbstractWebResource;
27 +
28 +import javax.ws.rs.GET;
29 +import javax.ws.rs.Path;
30 +import javax.ws.rs.PathParam;
31 +import javax.ws.rs.Produces;
32 +import javax.ws.rs.core.MediaType;
33 +import javax.ws.rs.core.Response;
34 +import java.util.Optional;
35 +import java.util.Set;
36 +
37 +import static org.onosproject.cpman.ControlResource.CONTROL_MESSAGE_METRICS;
38 +import static org.onosproject.cpman.ControlResource.CPU_METRICS;
39 +import static org.onosproject.cpman.ControlResource.DISK_METRICS;
40 +import static org.onosproject.cpman.ControlResource.MEMORY_METRICS;
41 +import static org.onosproject.cpman.ControlResource.NETWORK_METRICS;
42 +import static org.onosproject.cpman.ControlResource.Type.CONTROL_MESSAGE;
43 +import static org.onosproject.cpman.ControlResource.Type.DISK;
44 +import static org.onosproject.cpman.ControlResource.Type.NETWORK;
45 +
46 +/**
47 + * Query control metrics.
48 + */
49 +@Path("metrics")
50 +public class ControlMetricsWebResource extends AbstractWebResource {
51 +
52 + private final ControlPlaneMonitorService monitorService =
53 + get(ControlPlaneMonitorService.class);
54 + private final ClusterService clusterService = get(ClusterService.class);
55 + private final NodeId localNodeId = clusterService.getLocalNode().id();
56 + private final ObjectNode root = mapper().createObjectNode();
57 +
58 + /**
59 + * Returns control message metrics of all devices.
60 + *
61 + * @return array of all control message metrics
62 + */
63 + @GET
64 + @Path("messages")
65 + @Produces(MediaType.APPLICATION_JSON)
66 + public Response controlMessageMetrics() {
67 +
68 + ArrayNode deviceNodes = root.putArray("devices");
69 + monitorService.availableResources(CONTROL_MESSAGE).forEach(name -> {
70 + ObjectNode deviceNode = mapper().createObjectNode();
71 + ObjectNode valueNode = mapper().createObjectNode();
72 +
73 + metricsStats(monitorService, localNodeId, CONTROL_MESSAGE_METRICS,
74 + DeviceId.deviceId(name), valueNode);
75 + deviceNode.put("name", name);
76 + deviceNode.set("value", valueNode);
77 +
78 + deviceNodes.add(deviceNode);
79 + });
80 +
81 + return ok(root).build();
82 + }
83 +
84 + /**
85 + * Returns control message metrics of a given device.
86 + *
87 + * @param deviceId device identification
88 + * @return control message metrics of a given device
89 + */
90 + @GET
91 + @Produces(MediaType.APPLICATION_JSON)
92 + @Path("messages/{deviceId}")
93 + public Response controlMessageMetrics(@PathParam("deviceId") String deviceId) {
94 +
95 + metricsStats(monitorService, localNodeId, CONTROL_MESSAGE_METRICS,
96 + DeviceId.deviceId(deviceId), root);
97 +
98 + return ok(root).build();
99 + }
100 +
101 + /**
102 + * Returns cpu metrics.
103 + *
104 + * @return cpu metrics
105 + */
106 + @GET
107 + @Path("cpu_metrics")
108 + @Produces(MediaType.APPLICATION_JSON)
109 + public Response cpuMetrics() {
110 +
111 + metricsStats(monitorService, localNodeId, CPU_METRICS, root);
112 + return ok(root).build();
113 + }
114 +
115 + /**
116 + * Returns memory metrics.
117 + *
118 + * @return memory metrics
119 + */
120 + @GET
121 + @Path("memory_metrics")
122 + @Produces(MediaType.APPLICATION_JSON)
123 + public Response memoryMetrics() {
124 +
125 + metricsStats(monitorService, localNodeId, MEMORY_METRICS, root);
126 + return ok(root).build();
127 + }
128 +
129 + /**
130 + * Returns disk metrics of all resources.
131 + *
132 + * @return disk metrics of all resources
133 + */
134 + @GET
135 + @Path("disk_metrics")
136 + @Produces(MediaType.APPLICATION_JSON)
137 + public Response diskMetrics() {
138 +
139 + ArrayNode diskNodes = root.putArray("disks");
140 + monitorService.availableResources(DISK).forEach(name -> {
141 + ObjectNode diskNode = mapper().createObjectNode();
142 + ObjectNode valueNode = mapper().createObjectNode();
143 +
144 + metricsStats(monitorService, localNodeId, DISK_METRICS, name, valueNode);
145 + diskNode.put("name", name);
146 + diskNode.set("value", valueNode);
147 +
148 + diskNodes.add(diskNode);
149 + });
150 +
151 + return ok(root).build();
152 + }
153 +
154 + /**
155 + * Returns network metrics of all resources.
156 + *
157 + * @return network metrics of all resources
158 + */
159 + @GET
160 + @Path("network_metrics")
161 + @Produces(MediaType.APPLICATION_JSON)
162 + public Response networkMetrics() {
163 +
164 + ArrayNode networkNodes = root.putArray("networks");
165 + monitorService.availableResources(NETWORK).forEach(name -> {
166 + ObjectNode networkNode = mapper().createObjectNode();
167 + ObjectNode valueNode = mapper().createObjectNode();
168 +
169 + metricsStats(monitorService, localNodeId, NETWORK_METRICS, name, valueNode);
170 + networkNode.put("name", name);
171 + networkNode.set("value", valueNode);
172 +
173 + networkNodes.add(networkNode);
174 + });
175 +
176 + return ok(root).build();
177 + }
178 +
179 + /**
180 + * Returns a collection of control message stats.
181 + *
182 + * @param service control plane monitoring service
183 + * @param nodeId node identification
184 + * @param typeSet a set of control message types
185 + * @param did device identification
186 + * @param node object node
187 + * @return a collection of control message stats
188 + */
189 + private ArrayNode metricsStats(ControlPlaneMonitorService service,
190 + NodeId nodeId, Set<ControlMetricType> typeSet,
191 + DeviceId did, ObjectNode node) {
192 + return metricsStats(service, nodeId, typeSet, null, did, node);
193 + }
194 +
195 + /**
196 + * Returns a collection of system metric stats.
197 + *
198 + * @param service control plane monitoring service
199 + * @param nodeId node identification
200 + * @param typeSet a set of system metric types
201 + * @param node object node
202 + * @return a collection of system metric stats
203 + */
204 + private ArrayNode metricsStats(ControlPlaneMonitorService service,
205 + NodeId nodeId, Set<ControlMetricType> typeSet,
206 + ObjectNode node) {
207 + return metricsStats(service, nodeId, typeSet, null, null, node);
208 + }
209 +
210 + /**
211 + * Returns a collection of system metric stats.
212 + *
213 + * @param service control plane monitoring service
214 + * @param nodeId node identification
215 + * @param typeSet a set of control message types
216 + * @param resourceName device identification
217 + * @param node object node
218 + * @return a collection of system metric stats
219 + */
220 + private ArrayNode metricsStats(ControlPlaneMonitorService service,
221 + NodeId nodeId, Set<ControlMetricType> typeSet,
222 + String resourceName, ObjectNode node) {
223 + return metricsStats(service, nodeId, typeSet, resourceName, null, node);
224 + }
225 +
226 + /**
227 + * Returns a collection of control loads of the given control metric types.
228 + *
229 + * @param service control plane monitoring service
230 + * @param nodeId node identification
231 + * @param typeSet a group of control metric types
232 + * @param name resource name
233 + * @param did device identification
234 + * @return a collection of control loads
235 + */
236 + private ArrayNode metricsStats(ControlPlaneMonitorService service,
237 + NodeId nodeId, Set<ControlMetricType> typeSet,
238 + String name, DeviceId did, ObjectNode node) {
239 + ArrayNode metricsNode = node.putArray("metrics");
240 +
241 + if (name == null && did == null) {
242 + typeSet.forEach(type -> {
243 + ObjectNode metricNode = mapper().createObjectNode();
244 + ControlLoad load = service.getLoad(nodeId, type, Optional.ofNullable(null));
245 + if (load != null) {
246 + metricNode.set(type.toString().toLowerCase(), codec(ControlLoad.class)
247 + .encode(service.getLoad(nodeId, type, Optional.ofNullable(null)), this));
248 + metricsNode.add(metricNode);
249 + }
250 + });
251 + } else if (name == null) {
252 + typeSet.forEach(type -> {
253 + ObjectNode metricNode = mapper().createObjectNode();
254 + ControlLoad load = service.getLoad(nodeId, type, Optional.of(did));
255 + if (load != null) {
256 + metricNode.set(type.toString().toLowerCase(),
257 + codec(ControlLoad.class).encode(load, this));
258 + metricsNode.add(metricNode);
259 + }
260 + });
261 + } else if (did == null) {
262 + typeSet.forEach(type -> {
263 + ObjectNode metricNode = mapper().createObjectNode();
264 + ControlLoad load = service.getLoad(nodeId, type, name);
265 + if (load != null) {
266 + metricNode.set(type.toString().toLowerCase(),
267 + codec(ControlLoad.class).encode(load, this));
268 + metricsNode.add(metricNode);
269 + }
270 + });
271 + }
272 +
273 + return metricsNode;
274 + }
275 +}
...@@ -40,16 +40,16 @@ import java.util.Optional; ...@@ -40,16 +40,16 @@ import java.util.Optional;
40 import static org.onlab.util.Tools.nullIsIllegal; 40 import static org.onlab.util.Tools.nullIsIllegal;
41 41
42 /** 42 /**
43 - * Collect control plane metrics. 43 + * Collect system metrics.
44 */ 44 */
45 @Path("collector") 45 @Path("collector")
46 -public class ControlMetricsCollectorWebResource extends AbstractWebResource { 46 +public class SystemMetricsCollectorWebResource extends AbstractWebResource {
47 47
48 - final ControlPlaneMonitorService service = get(ControlPlaneMonitorService.class); 48 + private final ControlPlaneMonitorService service = get(ControlPlaneMonitorService.class);
49 - public static final int UPDATE_INTERVAL_IN_MINUTE = 1; 49 + private static final int UPDATE_INTERVAL_IN_MINUTE = 1;
50 - public static final String INVALID_SYSTEM_SPECS = "Invalid system specifications"; 50 + private static final String INVALID_SYSTEM_SPECS = "Invalid system specifications";
51 - public static final String INVALID_RESOURCE_NAME = "Invalid resource name"; 51 + private static final String INVALID_RESOURCE_NAME = "Invalid resource name";
52 - public static final String INVALID_REQUEST = "Invalid request"; 52 + private static final String INVALID_REQUEST = "Invalid request";
53 53
54 /** 54 /**
55 * Collects CPU metrics. 55 * Collects CPU metrics.
......
1 +/*
2 + * Copyright 2016 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.cpman.rest;
17 +
18 +import com.google.common.collect.ImmutableSet;
19 +import org.glassfish.jersey.server.ResourceConfig;
20 +import org.glassfish.jersey.test.JerseyTest;
21 +import org.junit.Before;
22 +import org.junit.Test;
23 +import org.onlab.osgi.ServiceDirectory;
24 +import org.onlab.osgi.TestServiceDirectory;
25 +import org.onlab.packet.IpAddress;
26 +import org.onlab.rest.BaseResource;
27 +import org.onosproject.cluster.ClusterService;
28 +import org.onosproject.cluster.ControllerNode;
29 +import org.onosproject.cluster.NodeId;
30 +import org.onosproject.codec.CodecService;
31 +import org.onosproject.codec.impl.CodecManager;
32 +import org.onosproject.cpman.ControlLoad;
33 +import org.onosproject.cpman.ControlPlaneMonitorService;
34 +import org.onosproject.cpman.codec.ControlLoadCodec;
35 +
36 +import javax.ws.rs.client.WebTarget;
37 +import java.util.Set;
38 +import java.util.concurrent.TimeUnit;
39 +
40 +import static org.easymock.EasyMock.anyObject;
41 +import static org.easymock.EasyMock.anyString;
42 +import static org.easymock.EasyMock.createMock;
43 +import static org.easymock.EasyMock.expect;
44 +import static org.easymock.EasyMock.replay;
45 +import static org.easymock.EasyMock.verify;
46 +import static org.hamcrest.Matchers.is;
47 +import static org.junit.Assert.assertThat;
48 +
49 +/**
50 + * Unit test for ControlMetrics REST API.
51 + */
52 +public class ControlMetricsResourceTest extends JerseyTest {
53 +
54 + final ControlPlaneMonitorService mockControlPlaneMonitorService =
55 + createMock(ControlPlaneMonitorService.class);
56 + final ClusterService mockClusterService = createMock(ClusterService.class);
57 + Set<String> resourceSet = ImmutableSet.of("resource1", "resource2");
58 + NodeId nodeId;
59 + ControlLoad mockControlLoad;
60 +
61 + private static final String PREFIX = "metrics";
62 +
63 + /**
64 + * Constructs a control metrics resource test instance.
65 + */
66 + public ControlMetricsResourceTest() {
67 + super(ResourceConfig.forApplicationClass(CPManWebApplication.class));
68 + }
69 +
70 + /**
71 + * Mock class for a controller node.
72 + */
73 + private static class MockControllerNode implements ControllerNode {
74 + final NodeId id;
75 +
76 + public MockControllerNode(NodeId id) {
77 + this.id = id;
78 + }
79 +
80 + @Override
81 + public NodeId id() {
82 + return this.id;
83 + }
84 +
85 + @Override
86 + public IpAddress ip() {
87 + return null;
88 + }
89 +
90 + @Override
91 + public int tcpPort() {
92 + return 0;
93 + }
94 + }
95 +
96 + private static class MockControlLoad implements ControlLoad {
97 +
98 + @Override
99 + public long average(int duration, TimeUnit unit) {
100 + return 0;
101 + }
102 +
103 + @Override
104 + public long average() {
105 + return 10L;
106 + }
107 +
108 + @Override
109 + public long[] recent(int duration, TimeUnit unit) {
110 + return new long[0];
111 + }
112 +
113 + @Override
114 + public long[] all() {
115 + return new long[0];
116 + }
117 +
118 + @Override
119 + public long rate() {
120 + return 0;
121 + }
122 +
123 + @Override
124 + public long latest() {
125 + return 10L;
126 + }
127 +
128 + @Override
129 + public boolean isValid() {
130 + return false;
131 + }
132 +
133 + @Override
134 + public long time() {
135 + return 20L;
136 + }
137 + }
138 +
139 + /**
140 + * Sets up the global values for all the tests.
141 + */
142 + @Before
143 + public void setUpTest() {
144 + final CodecManager codecService = new CodecManager();
145 + codecService.activate();
146 + codecService.registerCodec(ControlLoad.class, new ControlLoadCodec());
147 + ServiceDirectory testDirectory =
148 + new TestServiceDirectory()
149 + .add(ControlPlaneMonitorService.class,
150 + mockControlPlaneMonitorService)
151 + .add(ClusterService.class, mockClusterService)
152 + .add(CodecService.class, codecService);
153 + BaseResource.setServiceDirectory(testDirectory);
154 +
155 + nodeId = new NodeId("1");
156 + mockControlLoad = new MockControlLoad();
157 + ControllerNode mockControllerNode = new MockControllerNode(nodeId);
158 +
159 + expect(mockClusterService.getLocalNode()).andReturn(mockControllerNode).anyTimes();
160 + replay(mockClusterService);
161 + }
162 +
163 + /**
164 + * Tests the results of the REST API GET when there are no active entries.
165 + */
166 + @Test
167 + public void testResourceEmptyArray() {
168 + expect(mockControlPlaneMonitorService.availableResources(anyObject()))
169 + .andReturn(ImmutableSet.of()).once();
170 + replay(mockControlPlaneMonitorService);
171 + final WebTarget wt = target();
172 + final String response = wt.path(PREFIX + "/disk_metrics").request().get(String.class);
173 + assertThat(response, is("{\"disks\":[]}"));
174 +
175 + verify(mockControlPlaneMonitorService);
176 + }
177 +
178 + /**
179 + * Tests the results of the rest api GET when there are active metrics.
180 + */
181 + @Test
182 + public void testResourcePopulatedArray() {
183 + expect(mockControlPlaneMonitorService.availableResources(anyObject()))
184 + .andReturn(resourceSet).once();
185 + expect(mockControlPlaneMonitorService.getLoad(anyObject(), anyObject(),
186 + anyString())).andReturn(null).times(4);
187 + replay(mockControlPlaneMonitorService);
188 +
189 + final WebTarget wt = target();
190 + final String response = wt.path(PREFIX + "/disk_metrics").request().get(String.class);
191 + assertThat(response, is("{\"disks\":[{\"name\":\"resource1\",\"value\":{\"metrics\":[]}}," +
192 + "{\"name\":\"resource2\",\"value\":{\"metrics\":[]}}]}"));
193 + }
194 +}