/* * 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.SystemInfoFactory; import org.onosproject.cpman.impl.SystemMetricsAggregator; 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.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) 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.empty(), SYSTEM_TYPE); cm = new ControlMetric(ControlMetricType.CPU_LOAD, new MetricValue.Builder().load(cpuLoad).add()); monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.empty()); 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.empty()); 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.empty()); 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.empty()); 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.empty()); aggregator.increment(ControlMetricType.CPU_IDLE_TIME, cpuIdleTime); } catch (IOException e) { throw new IllegalArgumentException(e); } 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) 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.empty(), SYSTEM_TYPE); cm = new ControlMetric(ControlMetricType.MEMORY_USED_RATIO, new MetricValue.Builder().load(memUsedRatio).add()); monitorService.updateMetric(cm, UPDATE_INTERVAL_IN_MINUTE, Optional.empty()); 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.empty()); 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.empty()); 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.empty()); aggregator.increment(ControlMetricType.MEMORY_FREE, memFree); } catch (IOException e) { throw new IllegalArgumentException(e); } 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) 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); } 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) 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); } 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) 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); } 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); } }