blob: eedb403218ea871a8af10e56b6425acc9b8765f2 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* 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.drivers.server;
import org.onosproject.drivers.server.behavior.CpuStatisticsDiscovery;
import org.onosproject.drivers.server.behavior.MonitoringStatisticsDiscovery;
import org.onosproject.drivers.server.devices.CpuDevice;
import org.onosproject.drivers.server.devices.CpuVendor;
import org.onosproject.drivers.server.devices.nic.NicDevice;
import org.onosproject.drivers.server.devices.nic.NicRxFilter;
import org.onosproject.drivers.server.devices.nic.NicRxFilter.RxFilter;
import org.onosproject.drivers.server.devices.ServerDeviceDescription;
import org.onosproject.drivers.server.devices.RestServerSBDevice;
import org.onosproject.drivers.server.stats.CpuStatistics;
import org.onosproject.drivers.server.stats.MonitoringStatistics;
import org.onosproject.drivers.server.stats.TimingStatistics;
import org.onosproject.drivers.server.impl.devices.DefaultCpuDevice;
import org.onosproject.drivers.server.impl.devices.DefaultNicDevice;
import org.onosproject.drivers.server.impl.devices.DefaultRestServerSBDevice;
import org.onosproject.drivers.server.impl.devices.DefaultServerDeviceDescription;
import org.onosproject.drivers.server.impl.stats.DefaultCpuStatistics;
import org.onosproject.drivers.server.impl.stats.DefaultMonitoringStatistics;
import org.onosproject.drivers.server.impl.stats.DefaultTimingStatistics;
import org.onlab.packet.ChassisId;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.behaviour.DevicesDiscovery;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceDescriptionDiscovery;
import org.onosproject.net.device.DefaultPortStatistics;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.device.PortStatistics;
import org.onosproject.net.device.PortStatisticsDiscovery;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.protocol.rest.RestSBDevice;
import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
import org.slf4j.Logger;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.ImmutableList;
import javax.ws.rs.ProcessingException;
import java.io.InputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Discovers the device details of
* REST-based commodity server devices.
*/
public class ServerDevicesDiscovery extends BasicServerDriver
implements DevicesDiscovery, DeviceDescriptionDiscovery,
PortStatisticsDiscovery, CpuStatisticsDiscovery,
MonitoringStatisticsDiscovery {
private final Logger log = getLogger(getClass());
/**
* Resource endpoints of the server agent (REST server-side).
*/
private static final String RESOURCE_DISCOVERY_URL = BASE_URL + SLASH + "resources";
private static final String GLOBAL_STATS_URL = BASE_URL + SLASH + "stats";
private static final String SERVICE_CHAINS_STATS_URL = BASE_URL + SLASH + "chains_stats"; // + /ID
/**
* Parameters to be exchanged with the server's agent.
*/
private static final String PARAM_MANUFACTURER = "manufacturer";
private static final String PARAM_HW_VENDOR = "hwVersion";
private static final String PARAM_SW_VENDOR = "swVersion";
private static final String PARAM_SERIAL = "serial";
private static final String PARAM_TIMING_STATS = "timingStats";
private static final String PARAM_TIMING_AUTOSCALE = "autoScaleTimingStats";
private static final String NIC_PARAM_NAME = "name";
private static final String NIC_PARAM_PORT_INDEX = "index";
private static final String NIC_PARAM_PORT_TYPE = "portType";
private static final String NIC_PARAM_PORT_TYPE_FIBER = "fiber";
private static final String NIC_PARAM_PORT_TYPE_COPPER = "copper";
private static final String NIC_PARAM_SPEED = "speed";
private static final String NIC_PARAM_STATUS = "status";
private static final String NIC_PARAM_HW_ADDR = "hwAddr";
/**
* NIC statistics.
*/
private static final String NIC_STATS_TX_COUNT = "txCount";
private static final String NIC_STATS_TX_BYTES = "txBytes";
private static final String NIC_STATS_TX_DROPS = "txDropped";
private static final String NIC_STATS_TX_ERRORS = "txErrors";
private static final String NIC_STATS_RX_COUNT = "rxCount";
private static final String NIC_STATS_RX_BYTES = "rxBytes";
private static final String NIC_STATS_RX_DROPS = "rxDropped";
private static final String NIC_STATS_RX_ERRORS = "rxErrors";
/**
* CPU statistics.
*/
private static final String CPU_PARAM_ID = "id";
private static final String CPU_PARAM_VENDOR = "vendor";
private static final String CPU_PARAM_FREQUENCY = "frequency";
private static final String CPU_PARAM_LOAD = "load";
private static final String CPU_PARAM_QUEUE = "queue";
private static final String CPU_PARAM_STATUS = "busy";
private static final String CPU_PARAM_THROUGHPUT = "throughput";
private static final String CPU_PARAM_LATENCY = "latency";
private static final String MON_PARAM_UNIT = "unit";
private static final String MON_PARAM_BUSY_CPUS = "busyCpus";
private static final String MON_PARAM_FREE_CPUS = "freeCpus";
private static final String MON_PARAM_MIN = "min";
private static final String MON_PARAM_AVERAGE = "average";
private static final String MON_PARAM_MAX = "max";
/**
* Timing statistics.
*/
private static final String TIMING_PARAM_PARSE = "parseTime";
private static final String TIMING_PARAM_LAUNCH = "launchTime";
private static final String TIMING_PARAM_DEPLOY = "deployTime";
private static final String TIMING_PARAM_AUTOSCALE = "autoScaleTime";
/**
* Auxiliary constants.
*/
private static final short DISCOVERY_RETRIES = 3;
private static final String CPU_VENDOR_NULL = "Unsupported CPU vendor" +
" Choose one in: " + BasicServerDriver.enumTypesToString(CpuVendor.class);
private static final String NIC_RX_FILTER_NULL = "Unsupported NIC Rx filter" +
" Choose one in: " + BasicServerDriver.enumTypesToString(RxFilter.class);
/**
* Port types that usually appear in commodity servers.
*/
public static final Map<String, Port.Type> PORT_TYPE_MAP =
Collections.unmodifiableMap(
new HashMap<String, Port.Type>() {
{
put(NIC_PARAM_PORT_TYPE_FIBER, Port.Type.FIBER);
put(NIC_PARAM_PORT_TYPE_COPPER, Port.Type.COPPER);
}
}
);
/**
* Constructs server device discovery.
*/
public ServerDevicesDiscovery() {
super();
log.debug("Started");
}
@Override
public Set<DeviceId> deviceIds() {
// Set of devices to return
Set<DeviceId> devices = new HashSet<DeviceId>();
DeviceId deviceId = getHandler().data().deviceId();
checkNotNull(deviceId, DEVICE_ID_NULL);
devices.add(deviceId);
return devices;
}
@Override
public DeviceDescription deviceDetails(DeviceId deviceId) {
return getDeviceDetails(deviceId);
}
@Override
public DeviceDescription discoverDeviceDetails() {
return getDeviceDetails(null);
}
/**
* Query a server to retrieve its features.
*
* @param deviceId the device ID to be queried
* @return a DeviceDescription with the device's features
*/
private DeviceDescription getDeviceDetails(DeviceId deviceId) {
// Retrieve the device ID, if null given
if (deviceId == null) {
deviceId = getHandler().data().deviceId();
checkNotNull(deviceId, DEVICE_ID_NULL);
}
// Get the device
RestSBDevice device = getController().getDevice(deviceId);
checkNotNull(device, DEVICE_NULL);
// Hit the path that provides the server's resources
InputStream response = null;
try {
response = getController().get(deviceId, RESOURCE_DISCOVERY_URL, JSON);
} catch (ProcessingException pEx) {
log.error("Failed to discover the device details of: {}", deviceId);
return null;
}
// Load the JSON into objects
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> jsonMap = null;
JsonNode jsonNode = null;
ObjectNode objNode = null;
try {
jsonMap = mapper.readValue(response, Map.class);
jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
objNode = (ObjectNode) jsonNode;
} catch (IOException ioEx) {
log.error("Failed to discover the device details of: {}", deviceId);
return null;
}
if (jsonMap == null) {
log.error("Failed to discover the device details of: {}", deviceId);
return null;
}
// Get all the attributes
String id = get(jsonNode, BasicServerDriver.PARAM_ID);
String vendor = get(jsonNode, PARAM_MANUFACTURER);
String hw = get(jsonNode, PARAM_HW_VENDOR);
String sw = get(jsonNode, PARAM_SW_VENDOR);
String serial = get(jsonNode, PARAM_SERIAL);
// CPUs are composite attributes
Set<CpuDevice> cpuSet = new HashSet<CpuDevice>();
JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
// Construct CPU objects
for (JsonNode cn : cpuNode) {
ObjectNode cpuObjNode = (ObjectNode) cn;
// All the CPU attributes
int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
String cpuVendorStr = get(cn, CPU_PARAM_VENDOR);
long cpuFrequency = cpuObjNode.path(CPU_PARAM_FREQUENCY).asLong();
// Verify that this is a valid vendor
CpuVendor cpuVendor = CpuVendor.getByName(cpuVendorStr);
checkNotNull(cpuVendor, CPU_VENDOR_NULL);
// Construct a CPU device
CpuDevice cpu = new DefaultCpuDevice(cpuId, cpuVendor, cpuFrequency);
// Add it to the set
cpuSet.add(cpu);
}
// NICs are composite attributes too
Set<NicDevice> nicSet = new HashSet<NicDevice>();
JsonNode nicNode = objNode.path(PARAM_NICS);
DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
// Construct NIC objects
for (JsonNode nn : nicNode) {
ObjectNode nicObjNode = (ObjectNode) nn;
// All the NIC attributes
String nicName = get(nn, NIC_PARAM_NAME);
long nicIndex = nicObjNode.path(NIC_PARAM_PORT_INDEX).asLong();
long speed = nicObjNode.path(NIC_PARAM_SPEED).asLong();
String portTypeStr = get(nn, NIC_PARAM_PORT_TYPE);
Port.Type portType = PORT_TYPE_MAP.get(portTypeStr);
if (portType == null) {
throw new IllegalArgumentException(
portTypeStr + " is not a valid port type for NIC " + nicName);
}
boolean status = nicObjNode.path(NIC_PARAM_STATUS).asInt() > 0;
String hwAddr = get(nn, NIC_PARAM_HW_ADDR);
JsonNode tagNode = nicObjNode.path(BasicServerDriver.NIC_PARAM_RX_FILTER);
if (tagNode == null) {
throw new IllegalArgumentException(
"The Rx filters of NIC " + nicName + " are not reported");
}
// Convert the JSON list into an array of strings
List<String> rxFilters = null;
try {
rxFilters = mapper.readValue(tagNode.traverse(),
new TypeReference<ArrayList<String>>() { });
} catch (IOException ioEx) {
continue;
}
// Parse the array of strings and create an RxFilter object
NicRxFilter rxFilterMechanism = new NicRxFilter();
for (String s : rxFilters) {
// Verify that this is a valid Rx filter
RxFilter rf = RxFilter.getByName(s);
checkNotNull(rf, NIC_RX_FILTER_NULL);
rxFilterMechanism.addRxFilter(rf);
}
// Store NIC name to number mapping as an annotation
annotations.set(nicName, Long.toString(nicIndex));
// Construct a NIC device for this server
NicDevice nic = new DefaultNicDevice(
nicName, nicIndex, portType, speed, status, hwAddr, rxFilterMechanism);
// Add it to the set
nicSet.add(nic);
}
// Construct a complete server device object.
// Lists of NICs and CPUs extend the information
// already in RestSBDevice (parent class).
RestServerSBDevice dev = new DefaultRestServerSBDevice(
device.ip(), device.port(), device.username(),
device.password(), device.protocol(), device.url(),
device.isActive(), device.testUrl().orElse(""),
vendor, hw, sw, AuthenticationScheme.BASIC, "",
cpuSet, nicSet
);
checkNotNull(dev, DEVICE_NULL);
// Set alive
raiseDeviceReconnect(dev);
// Updates the controller with the complete device information
getController().removeDevice(deviceId);
getController().addDevice((RestSBDevice) dev);
// Create a description for this server device
ServerDeviceDescription desc = null;
try {
desc = new DefaultServerDeviceDescription(
new URI(id), Device.Type.SERVER, vendor,
hw, sw, serial, new ChassisId(),
cpuSet, nicSet, annotations.build()
);
} catch (URISyntaxException uEx) {
log.error("Failed to create a server device description for: {}",
deviceId);
return null;
}
log.info("Device's {} details sent to the controller", deviceId);
return desc;
}
@Override
public List<PortDescription> discoverPortDetails() {
// Retrieve the device ID
DeviceId deviceId = getHandler().data().deviceId();
checkNotNull(deviceId, DEVICE_ID_NULL);
// .. and object
RestServerSBDevice device = null;
try {
device = (RestServerSBDevice) getController().getDevice(deviceId);
} catch (ClassCastException ccEx) {
log.error("Failed to discover ports for device {}", deviceId);
return Collections.EMPTY_LIST;
}
if (device == null) {
log.error("No device with ID {} is available for port discovery", deviceId);
return Collections.EMPTY_LIST;
}
if ((device.nics() == null) || (device.nics().size() == 0)) {
log.error("No ports available on {}", deviceId);
return Collections.EMPTY_LIST;
}
// List of port descriptions to return
List<PortDescription> portDescriptions = Lists.newArrayList();
// Sorted list of NIC ports
Set<NicDevice> nics = new TreeSet(device.nics());
// Iterate through the NICs of this device to populate the list
for (NicDevice nic : nics) {
// Include the name of this device as an annotation
DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
.set(AnnotationKeys.PORT_NAME, nic.name());
// Create a port description and add it to the list
portDescriptions.add(
DefaultPortDescription.builder()
.withPortNumber(PortNumber.portNumber(nic.portNumber(), nic.name()))
.isEnabled(nic.status())
.type(nic.portType())
.portSpeed(nic.speed())
.annotations(annotations.build())
.build());
log.info("Port discovery on device {}: NIC {} is {} at {} Mbps",
deviceId, nic.portNumber(), nic.status() ? "up" : "down",
nic.speed());
}
return ImmutableList.copyOf(portDescriptions);
}
@Override
public Collection<PortStatistics> discoverPortStatistics() {
// Retrieve the device ID
DeviceId deviceId = getHandler().data().deviceId();
checkNotNull(deviceId, DEVICE_ID_NULL);
// Get port statistics for this device
return getPortStatistics(deviceId);
}
/**
* Query a server to retrieve its port statistics.
*
* @param deviceId the device ID to be queried
* @return list of (per port) PortStatistics
*/
private Collection<PortStatistics> getPortStatistics(DeviceId deviceId) {
// Get global monitoring statistics
MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
if (monStats == null) {
return Collections.EMPTY_LIST;
}
// Filter out the NIC statistics
Collection<PortStatistics> portStats = monStats.nicStatisticsAll();
if (portStats == null) {
return Collections.EMPTY_LIST;
}
log.debug("Port statistics: {}", portStats.toString());
return portStats;
}
@Override
public Collection<CpuStatistics> discoverCpuStatistics() {
// Retrieve the device ID
DeviceId deviceId = getHandler().data().deviceId();
checkNotNull(deviceId, DEVICE_ID_NULL);
// Get CPU statistics for this device
return getCpuStatistics(deviceId);
}
/**
* Query a server to retrieve its CPU statistics.
*
* @param deviceId the device ID to be queried
* @return list of (per core) CpuStatistics
*/
public Collection<CpuStatistics> getCpuStatistics(DeviceId deviceId) {
// Get global monitoring statistics
MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
if (monStats == null) {
return Collections.EMPTY_LIST;
}
// Filter out the CPU statistics
Collection<CpuStatistics> cpuStats = monStats.cpuStatisticsAll();
if (cpuStats == null) {
return Collections.EMPTY_LIST;
}
log.debug("CPU statistics: {}", cpuStats.toString());
return cpuStats;
}
@Override
public MonitoringStatistics discoverGlobalMonitoringStatistics() {
// Retrieve the device ID
DeviceId deviceId = getHandler().data().deviceId();
checkNotNull(deviceId, DEVICE_ID_NULL);
// Get global monitoring statistics for this device
return getGlobalMonitoringStatistics(deviceId);
}
/**
* Query a server to retrieve its global monitoring statistics.
*
* @param deviceId the device ID to be queried
* @return global monitoring statistics
*/
public MonitoringStatistics getGlobalMonitoringStatistics(DeviceId deviceId) {
// Monitoring statistics to return
MonitoringStatistics monStats = null;
RestServerSBDevice device = null;
try {
device = (RestServerSBDevice) getController().getDevice(deviceId);
} catch (ClassCastException ccEx) {
log.error("Failed to retrieve global monitoring statistics from device {}",
deviceId);
return monStats;
}
if ((device == null) || (!device.isActive())) {
return monStats;
}
// Hit the path that provides the server's global resources
InputStream response = null;
try {
response = getController().get(deviceId, GLOBAL_STATS_URL, JSON);
} catch (ProcessingException pEx) {
log.error("Failed to retrieve global monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
// Load the JSON into objects
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> jsonMap = null;
ObjectNode objNode = null;
try {
jsonMap = mapper.readValue(response, Map.class);
JsonNode jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
objNode = (ObjectNode) jsonNode;
} catch (IOException ioEx) {
log.error("Failed to retrieve global monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
if (jsonMap == null) {
log.error("Failed to retrieve global monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
// Get high-level CPU statistics
int busyCpus = objNode.path(MON_PARAM_BUSY_CPUS).asInt();
int freeCpus = objNode.path(MON_PARAM_FREE_CPUS).asInt();
// Get a list of CPU statistics per core
Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
// Get a list of port statistics
Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
// Get zero timing statistics
TimingStatistics timinsgStats = getZeroTimingStatistics();
// Ready to construct the grand object
DefaultMonitoringStatistics.Builder statsBuilder =
DefaultMonitoringStatistics.builder();
statsBuilder.setDeviceId(deviceId)
.setTimingStatistics(timinsgStats)
.setCpuStatistics(cpuStats)
.setNicStatistics(nicStats);
monStats = statsBuilder.build();
// When a device reports monitoring data, it means it is alive
raiseDeviceReconnect(device);
log.debug("Global monitoring statistics: {}", monStats.toString());
return monStats;
}
@Override
public MonitoringStatistics discoverMonitoringStatistics(URI tcId) {
// Retrieve the device ID
DeviceId deviceId = getHandler().data().deviceId();
checkNotNull(deviceId, DEVICE_ID_NULL);
// Get resource-specific monitoring statistics for this device
return getMonitoringStatistics(deviceId, tcId);
}
/**
* Query a server to retrieve monitoring statistics for a
* specific resource (i.e., traffic class).
*
* @param deviceId the device ID to be queried
* @param tcId the ID of the traffic class to be monitored
* @return resource-specific monitoring statistics
*/
private MonitoringStatistics getMonitoringStatistics(DeviceId deviceId, URI tcId) {
// Monitoring statistics to return
MonitoringStatistics monStats = null;
RestServerSBDevice device = null;
try {
device = (RestServerSBDevice) getController().getDevice(deviceId);
} catch (ClassCastException ccEx) {
log.error("Failed to retrieve monitoring statistics from device {}",
deviceId);
return monStats;
}
if (device == null) {
return monStats;
}
// Create a resource-specific URL
String scUrl = SERVICE_CHAINS_STATS_URL + SLASH + tcId.toString();
// Hit the path that provides the server's specific resources
InputStream response = null;
try {
response = getController().get(deviceId, scUrl, JSON);
} catch (ProcessingException pEx) {
log.error("Failed to retrieve monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
// Load the JSON into objects
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> jsonMap = null;
JsonNode jsonNode = null;
ObjectNode objNode = null;
try {
jsonMap = mapper.readValue(response, Map.class);
jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
objNode = (ObjectNode) jsonNode;
} catch (IOException ioEx) {
log.error("Failed to retrieve monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
if (jsonMap == null) {
log.error("Failed to retrieve monitoring statistics from device {}",
deviceId);
raiseDeviceDisconnect(device);
return monStats;
}
// Get the ID of the traffic class
String id = get(jsonNode, PARAM_ID);
// And verify that this is the traffic class we want to monitor
if (!id.equals(tcId.toString())) {
throw new IllegalStateException(
"Failed to retrieve monitoring data for traffic class " + tcId +
". Traffic class ID does not agree."
);
}
// Get a list of CPU statistics per core
Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
// Get a list of port statistics
Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
// Get timing statistics
TimingStatistics timinsgStats = parseTimingStatistics(objNode);
// Ready to construct the grand object
DefaultMonitoringStatistics.Builder statsBuilder =
DefaultMonitoringStatistics.builder();
statsBuilder.setDeviceId(deviceId)
.setTimingStatistics(timinsgStats)
.setCpuStatistics(cpuStats)
.setNicStatistics(nicStats);
monStats = statsBuilder.build();
// When a device reports monitoring data, it means it is alive
raiseDeviceReconnect(device);
log.debug("Monitoring statistics: {}", monStats.toString());
return monStats;
}
/**
* Parse the input JSON object, looking for CPU-related
* statistics. Upon success, construct and return a list
* of CPU statistics objects.
*
* @param deviceId the device ID that sent the JSON object
* @param objNode input JSON node with CPU statistics information
* @return list of (per core) CpuStatistics
*/
private Collection<CpuStatistics> parseCpuStatistics(DeviceId deviceId, JsonNode objNode) {
if ((deviceId == null) || (objNode == null)) {
return Collections.EMPTY_LIST;
}
Collection<CpuStatistics> cpuStats = Lists.newArrayList();
JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
for (JsonNode cn : cpuNode) {
ObjectNode cpuObjNode = (ObjectNode) cn;
// CPU statistics builder
DefaultCpuStatistics.Builder cpuBuilder = DefaultCpuStatistics.builder();
// Throughput statistics are optional
JsonNode throughputNode = cpuObjNode.get(CPU_PARAM_THROUGHPUT);
if (throughputNode != null) {
String throughputUnit = get(throughputNode, MON_PARAM_UNIT);
if (!Strings.isNullOrEmpty(throughputUnit)) {
cpuBuilder.setThroughputUnit(throughputUnit);
}
float averageThroughput = (float) 0;
if (throughputNode.get(MON_PARAM_AVERAGE) != null) {
averageThroughput = throughputNode.path(MON_PARAM_AVERAGE).floatValue();
}
cpuBuilder.setAverageThroughput(averageThroughput);
}
// Latency statistics are optional
JsonNode latencyNode = cpuObjNode.get(CPU_PARAM_LATENCY);
if (latencyNode != null) {
String latencyUnit = get(latencyNode, MON_PARAM_UNIT);
if (!Strings.isNullOrEmpty(latencyUnit)) {
cpuBuilder.setLatencyUnit(latencyUnit);
}
float minLatency = (float) 0;
if (latencyNode.get(MON_PARAM_MIN) != null) {
minLatency = latencyNode.path(MON_PARAM_MIN).floatValue();
}
float averageLatency = (float) 0;
if (latencyNode.get(MON_PARAM_AVERAGE) != null) {
averageLatency = latencyNode.path(MON_PARAM_AVERAGE).floatValue();
}
float maxLatency = (float) 0;
if (latencyNode.get(MON_PARAM_MAX) != null) {
maxLatency = latencyNode.path(MON_PARAM_MAX).floatValue();
}
cpuBuilder.setMinLatency(minLatency)
.setAverageLatency(averageLatency)
.setMaxLatency(maxLatency);
}
// CPU ID with its load and status
int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
float cpuLoad = cpuObjNode.path(CPU_PARAM_LOAD).floatValue();
int queueId = cpuObjNode.path(CPU_PARAM_QUEUE).asInt();
int busySince = cpuObjNode.path(CPU_PARAM_STATUS).asInt();
// This is mandatory information
cpuBuilder.setDeviceId(deviceId)
.setId(cpuId)
.setLoad(cpuLoad)
.setQueue(queueId)
.setBusySince(busySince);
// We have all the statistics for this CPU core
cpuStats.add(cpuBuilder.build());
}
return cpuStats;
}
/**
* Parse the input JSON object, looking for NIC-related
* statistics. Upon success, construct and return a list
* of NIC statistics objects.
*
* @param deviceId the device ID that sent the JSON object
* @param objNode input JSON node with NIC statistics information
* @return list of (per port) PortStatistics
*/
private Collection<PortStatistics> parseNicStatistics(DeviceId deviceId, JsonNode objNode) {
if ((deviceId == null) || (objNode == null)) {
return Collections.EMPTY_LIST;
}
RestServerSBDevice device = null;
try {
device = (RestServerSBDevice) getController().getDevice(deviceId);
} catch (ClassCastException ccEx) {
return Collections.EMPTY_LIST;
}
if (device == null) {
return Collections.EMPTY_LIST;
}
Collection<PortStatistics> nicStats = Lists.newArrayList();
JsonNode nicNode = objNode.path(PARAM_NICS);
for (JsonNode nn : nicNode) {
ObjectNode nicObjNode = (ObjectNode) nn;
// All the NIC attributes
String nicName = get(nn, NIC_PARAM_NAME);
checkArgument(!Strings.isNullOrEmpty(nicName), "NIC name is empty or NULL");
long portNumber = device.portNumberFromName(nicName);
checkArgument(portNumber >= 0, "Unknown port ID " + portNumber + " for NIC " + nicName);
long rxCount = nicObjNode.path(NIC_STATS_RX_COUNT).asLong();
long rxBytes = nicObjNode.path(NIC_STATS_RX_BYTES).asLong();
long rxDropped = nicObjNode.path(NIC_STATS_RX_DROPS).asLong();
long rxErrors = nicObjNode.path(NIC_STATS_RX_ERRORS).asLong();
long txCount = nicObjNode.path(NIC_STATS_TX_COUNT).asLong();
long txBytes = nicObjNode.path(NIC_STATS_TX_BYTES).asLong();
long txDropped = nicObjNode.path(NIC_STATS_TX_DROPS).asLong();
long txErrors = nicObjNode.path(NIC_STATS_TX_ERRORS).asLong();
// Incorporate these statistics into an object
DefaultPortStatistics.Builder nicBuilder = DefaultPortStatistics.builder();
nicBuilder.setDeviceId(deviceId)
.setPort(PortNumber.portNumber(portNumber))
.setPacketsReceived(rxCount)
.setPacketsSent(txCount)
.setBytesReceived(rxBytes)
.setBytesSent(txBytes)
.setPacketsRxDropped(rxDropped)
.setPacketsRxErrors(rxErrors)
.setPacketsTxDropped(txDropped)
.setPacketsTxErrors(txErrors);
// We have statistics for this NIC
nicStats.add(nicBuilder.build());
}
return nicStats;
}
/**
* Parse the input JSON object, looking for timing-related statistics.
* Upon success, return a timing statistics object with the advertized values.
* Upon failure, return a timing statistics object with zero-initialized values.
*
* @param objNode input JSON node with timing statistics information
* @return TimingStatistics object or null
*/
private TimingStatistics parseTimingStatistics(JsonNode objNode) {
TimingStatistics timinsgStats = null;
if (objNode == null) {
return timinsgStats;
}
// If no timing statistics are present, then send zeros
if (objNode.get(PARAM_TIMING_STATS) == null) {
return getZeroTimingStatistics();
}
DefaultTimingStatistics.Builder timingBuilder = DefaultTimingStatistics.builder();
// Get timing statistics
JsonNode timingNode = objNode.path(PARAM_TIMING_STATS);
ObjectNode timingObjNode = (ObjectNode) timingNode;
// The unit of timing statistics
String timingStatsUnit = get(timingNode, MON_PARAM_UNIT);
if (!Strings.isNullOrEmpty(timingStatsUnit)) {
timingBuilder.setUnit(timingStatsUnit);
}
// Time (ns) to parse the controller's deployment instruction
long parsingTime = 0;
if (timingObjNode.get(TIMING_PARAM_PARSE) != null) {
parsingTime = timingObjNode.path(TIMING_PARAM_PARSE).asLong();
}
// Time (ns) to do the deployment
long launchingTime = 0;
if (timingObjNode.get(TIMING_PARAM_LAUNCH) != null) {
launchingTime = timingObjNode.path(TIMING_PARAM_LAUNCH).asLong();
}
// Deployment time (ns) equals to time to parse + time to launch
long deployTime = 0;
if (timingObjNode.get(TIMING_PARAM_DEPLOY) != null) {
deployTime = timingObjNode.path(TIMING_PARAM_DEPLOY).asLong();
}
checkArgument(deployTime == parsingTime + launchingTime, "Inconsistent timing statistics");
timingBuilder.setParsingTime(parsingTime)
.setLaunchingTime(launchingTime);
// Get autoscale timing statistics
JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_AUTOSCALE);
if (autoscaleTimingNode == null) {
return timingBuilder.build();
}
ObjectNode autoScaleTimingObjNode = (ObjectNode) autoscaleTimingNode;
// Time (ns) to autoscale a server's load
long autoScaleTime = 0;
if (autoScaleTimingObjNode.get(TIMING_PARAM_AUTOSCALE) != null) {
autoScaleTime = autoScaleTimingObjNode.path(TIMING_PARAM_AUTOSCALE).asLong();
}
timingBuilder.setAutoScaleTime(autoScaleTime);
return timingBuilder.build();
}
/**
* Return a timing statistics object with zero counters.
* This is useful when constructing MonitoringStatistics
* objects that do not require timers.
*
* @return TimingStatistics object
*/
private TimingStatistics getZeroTimingStatistics() {
DefaultTimingStatistics.Builder zeroTimingBuilder = DefaultTimingStatistics.builder();
zeroTimingBuilder.setParsingTime(0)
.setLaunchingTime(0)
.setAutoScaleTime(0);
return zeroTimingBuilder.build();
}
}