blob: c96d1d95fd5c2767829fade01946a764c25b4513 [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.cpman.rest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.onlab.metrics.MetricsService;
import org.onosproject.cpman.ControlMetric;
import org.onosproject.cpman.ControlMetricType;
import org.onosproject.cpman.ControlPlaneMonitorService;
import org.onosproject.cpman.ControlResource;
import org.onosproject.cpman.MetricValue;
import org.onosproject.cpman.SystemInfo;
import org.onosproject.cpman.impl.DefaultSystemInfo;
import org.onosproject.cpman.impl.SystemMetricsAggregator;
import org.onosproject.cpman.impl.SystemInfoFactory;
import org.onosproject.rest.AbstractWebResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import static org.onlab.util.Tools.nullIsIllegal;
/**
* Collect system metrics.
*/
@Path("collector")
public class SystemMetricsCollectorWebResource extends AbstractWebResource {
private final Logger log = LoggerFactory.getLogger(getClass());
private final ControlPlaneMonitorService monitorService = get(ControlPlaneMonitorService.class);
private final MetricsService metricsService = get(MetricsService.class);
private static final int UPDATE_INTERVAL_IN_MINUTE = 1;
private static final String INVALID_SYSTEM_SPECS = "Invalid system specifications";
private static final String INVALID_RESOURCE_NAME = "Invalid resource name";
private static final String INVALID_REQUEST = "Invalid request";
private static final int PERCENT_CONSTANT = 100;
private static final String SYSTEM_TYPE = "system";
private static final String DISK_TYPE = "disk";
private static final String NETWORK_TYPE = "network";
private static final Set<String> MEMORY_FIELD_SET = ControlResource.MEMORY_METRICS
.stream().map(type -> toCamelCase(type.toString(), true))
.collect(Collectors.toSet());
private static final Set<String> CPU_FIELD_SET = ControlResource.CPU_METRICS
.stream().map(type -> toCamelCase(type.toString(), true))
.collect(Collectors.toSet());
private SystemMetricsAggregator aggregator = SystemMetricsAggregator.getInstance();
/**
* Collects CPU metrics.
*
* @param stream JSON stream
* @return 200 OK
* @onos.rsModel CpuMetricsPost
*/
@POST
@Path("cpu_metrics")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response cpuMetrics(InputStream stream) {
ObjectNode root = mapper().createObjectNode();
ControlMetric cm;
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
if (jsonTree == null || !checkFields(jsonTree, CPU_FIELD_SET)) {
ok(root).build();
}
long cpuLoad = nullIsIllegal((long) (jsonTree.get("cpuLoad").asDouble()
* PERCENT_CONSTANT), INVALID_REQUEST);
long totalCpuTime = nullIsIllegal(jsonTree.get("totalCpuTime").asLong(), INVALID_REQUEST);
long sysCpuTime = nullIsIllegal(jsonTree.get("sysCpuTime").asLong(), INVALID_REQUEST);
long userCpuTime = nullIsIllegal(jsonTree.get("userCpuTime").asLong(), INVALID_REQUEST);
long cpuIdleTime = nullIsIllegal(jsonTree.get("cpuIdleTime").asLong(), INVALID_REQUEST);
aggregator.setMetricsService(metricsService);
aggregator.addMetrics(Optional.ofNullable(null), SYSTEM_TYPE);
cm = new ControlMetric(ControlMetricType.CPU_LOAD,
new MetricValue.Builder().load(cpuLoad).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.ofNullable(null));
aggregator.increment(ControlMetricType.CPU_LOAD, cpuLoad);
cm = new ControlMetric(ControlMetricType.TOTAL_CPU_TIME,
new MetricValue.Builder().load(totalCpuTime).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.ofNullable(null));
aggregator.increment(ControlMetricType.TOTAL_CPU_TIME, totalCpuTime);
cm = new ControlMetric(ControlMetricType.SYS_CPU_TIME,
new MetricValue.Builder().load(sysCpuTime).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.ofNullable(null));
aggregator.increment(ControlMetricType.SYS_CPU_TIME, sysCpuTime);
cm = new ControlMetric(ControlMetricType.USER_CPU_TIME,
new MetricValue.Builder().load(userCpuTime).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.ofNullable(null));
aggregator.increment(ControlMetricType.USER_CPU_TIME, userCpuTime);
cm = new ControlMetric(ControlMetricType.CPU_IDLE_TIME,
new MetricValue.Builder().load(cpuIdleTime).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.ofNullable(null));
aggregator.increment(ControlMetricType.CPU_IDLE_TIME, cpuIdleTime);
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
return ok(root).build();
}
/**
* Collects memory metrics.
*
* @param stream JSON stream
* @return 200 OK
* @onos.rsModel MemoryMetricsPost
*/
@POST
@Path("memory_metrics")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response memoryMetrics(InputStream stream) {
ObjectNode root = mapper().createObjectNode();
ControlMetric cm;
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
if (jsonTree == null || !checkFields(jsonTree, MEMORY_FIELD_SET)) {
ok(root).build();
}
long memUsed = nullIsIllegal(jsonTree.get("memoryUsed").asLong(), INVALID_REQUEST);
long memFree = nullIsIllegal(jsonTree.get("memoryFree").asLong(), INVALID_REQUEST);
long memTotal = memUsed + memFree;
long memUsedRatio = memTotal == 0L ? 0L : (memUsed * PERCENT_CONSTANT) / memTotal;
long memFreeRatio = memTotal == 0L ? 0L : (memFree * PERCENT_CONSTANT) / memTotal;
aggregator.setMetricsService(metricsService);
aggregator.addMetrics(Optional.ofNullable(null), SYSTEM_TYPE);
cm = new ControlMetric(ControlMetricType.MEMORY_USED_RATIO,
new MetricValue.Builder().load(memUsedRatio).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.ofNullable(null));
aggregator.increment(ControlMetricType.MEMORY_USED_RATIO, memUsedRatio);
cm = new ControlMetric(ControlMetricType.MEMORY_FREE_RATIO,
new MetricValue.Builder().load(memFreeRatio).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.ofNullable(null));
aggregator.increment(ControlMetricType.MEMORY_FREE_RATIO, memFreeRatio);
cm = new ControlMetric(ControlMetricType.MEMORY_USED,
new MetricValue.Builder().load(memUsed).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.ofNullable(null));
aggregator.increment(ControlMetricType.MEMORY_USED, memUsed);
cm = new ControlMetric(ControlMetricType.MEMORY_FREE,
new MetricValue.Builder().load(memFree).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.ofNullable(null));
aggregator.increment(ControlMetricType.MEMORY_FREE, memFree);
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
return ok(root).build();
}
/**
* Collects disk metrics.
*
* @param stream JSON stream
* @return 200 OK
* @onos.rsModel DiskMetricsPost
*/
@POST
@Path("disk_metrics")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response diskMetrics(InputStream stream) {
ObjectNode root = mapper().createObjectNode();
ControlMetric cm;
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
ArrayNode diskRes =
jsonTree.get("disks") == null ?
mapper().createArrayNode() : (ArrayNode) jsonTree.get("disks");
for (JsonNode node : diskRes) {
JsonNode resourceName = node.get("resourceName");
nullIsIllegal(resourceName, INVALID_RESOURCE_NAME);
aggregator.setMetricsService(metricsService);
aggregator.addMetrics(Optional.of(resourceName.asText()), DISK_TYPE);
long readBytes = nullIsIllegal(node.get("readBytes").asLong(), INVALID_REQUEST);
long writeBytes = nullIsIllegal(node.get("writeBytes").asLong(), INVALID_REQUEST);
cm = new ControlMetric(ControlMetricType.DISK_READ_BYTES,
new MetricValue.Builder().load(readBytes).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, resourceName.asText());
aggregator.increment(resourceName.asText(), DISK_TYPE,
ControlMetricType.DISK_READ_BYTES, readBytes);
cm = new ControlMetric(ControlMetricType.DISK_WRITE_BYTES,
new MetricValue.Builder().load(writeBytes).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, resourceName.asText());
aggregator.increment(resourceName.asText(), DISK_TYPE,
ControlMetricType.DISK_WRITE_BYTES, writeBytes);
}
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
return ok(root).build();
}
/**
* Collects network metrics.
*
* @param stream JSON stream
* @return 200 OK
* @onos.rsModel NetworkMetricsPost
*/
@POST
@Path("network_metrics")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response networkMetrics(InputStream stream) {
ObjectNode root = mapper().createObjectNode();
ControlMetric cm;
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
ArrayNode networkRes = jsonTree.get("networks") == null
? mapper().createArrayNode() : (ArrayNode) jsonTree.get("networks");
for (JsonNode node : networkRes) {
JsonNode resourceName = node.get("resourceName");
nullIsIllegal(resourceName, INVALID_RESOURCE_NAME);
aggregator.setMetricsService(metricsService);
aggregator.addMetrics(Optional.of(resourceName.asText()), NETWORK_TYPE);
long inBytes = nullIsIllegal(node.get("incomingBytes").asLong(), INVALID_REQUEST);
long outBytes = nullIsIllegal(node.get("outgoingBytes").asLong(), INVALID_REQUEST);
long inPackets = nullIsIllegal(node.get("incomingPackets").asLong(), INVALID_REQUEST);
long outPackets = nullIsIllegal(node.get("outgoingPackets").asLong(), INVALID_REQUEST);
cm = new ControlMetric(ControlMetricType.NW_INCOMING_BYTES,
new MetricValue.Builder().load(inBytes).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, resourceName.asText());
aggregator.increment(resourceName.asText(), NETWORK_TYPE,
ControlMetricType.NW_INCOMING_BYTES, inBytes);
cm = new ControlMetric(ControlMetricType.NW_OUTGOING_BYTES,
new MetricValue.Builder().load(outBytes).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, resourceName.asText());
aggregator.increment(resourceName.asText(), NETWORK_TYPE,
ControlMetricType.NW_OUTGOING_BYTES, outBytes);
cm = new ControlMetric(ControlMetricType.NW_INCOMING_PACKETS,
new MetricValue.Builder().load(inPackets).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, resourceName.asText());
aggregator.increment(resourceName.asText(), NETWORK_TYPE,
ControlMetricType.NW_INCOMING_PACKETS, inPackets);
cm = new ControlMetric(ControlMetricType.NW_OUTGOING_PACKETS,
new MetricValue.Builder().load(outPackets).add());
monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, resourceName.asText());
aggregator.increment(resourceName.asText(), NETWORK_TYPE,
ControlMetricType.NW_OUTGOING_PACKETS, outPackets);
}
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
return ok(root).build();
}
/**
* Collects system information.
* The system information includes the various control metrics
* which do not require aggregation.
*
* @param stream JSON stream
* @return 200 OK
* @onos.rsModel SystemInfoPost
*/
@POST
@Path("system_info")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response systemInfo(InputStream stream) {
ObjectNode root = mapper().createObjectNode();
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
JsonNode numOfCores = jsonTree.get("numOfCores");
JsonNode numOfCpus = jsonTree.get("numOfCpus");
JsonNode cpuSpeed = jsonTree.get("cpuSpeed");
JsonNode totalMemory = jsonTree.get("totalMemory");
if (numOfCores != null && numOfCpus != null &&
cpuSpeed != null && totalMemory != null) {
SystemInfo systemInfo = new DefaultSystemInfo.Builder()
.numOfCores(numOfCores.asInt())
.numOfCpus(numOfCpus.asInt())
.cpuSpeed(cpuSpeed.asInt())
.totalMemory(totalMemory.asInt())
.build();
// try to store the system info.
SystemInfoFactory.getInstance().setSystemInfo(systemInfo);
} else {
throw new IllegalArgumentException(INVALID_SYSTEM_SPECS);
}
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
return ok(root).build();
}
private boolean checkFields(ObjectNode node, Set<String> original) {
Iterator<String> fieldNames = node.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
if (!original.contains(fieldName) || node.get(fieldName) == null) {
log.warn("Illegal field name: {}", fieldName);
return false;
}
}
return true;
}
private static String toCamelCase(String value, boolean startWithLowerCase) {
String[] strings = StringUtils.split(value.toLowerCase(), "_");
for (int i = startWithLowerCase ? 1 : 0; i < strings.length; i++) {
strings[i] = StringUtils.capitalize(strings[i]);
}
return StringUtils.join(strings);
}
}