blob: 346c8ca96570205b4a0d3bdc4154b23855467a3d [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.cpu.CpuCacheHierarchyDevice;
import org.onosproject.drivers.server.devices.cpu.CpuDevice;
import org.onosproject.drivers.server.devices.memory.MemoryHierarchyDevice;
import org.onosproject.drivers.server.devices.nic.NicDevice;
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.MemoryStatistics;
import org.onosproject.drivers.server.stats.MonitoringStatistics;
import org.onosproject.drivers.server.stats.TimingStatistics;
import org.onosproject.drivers.server.impl.devices.DefaultBasicCpuCacheDevice;
import org.onosproject.drivers.server.impl.devices.DefaultCpuCacheHierarchyDevice;
import org.onosproject.drivers.server.impl.devices.DefaultCpuDevice;
import org.onosproject.drivers.server.impl.devices.DefaultMemoryHierarchyDevice;
import org.onosproject.drivers.server.impl.devices.DefaultMemoryModuleDevice;
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.DefaultMemoryStatistics;
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.behaviour.DeviceCpuStats;
import org.onosproject.net.behaviour.DeviceMemoryStats;
import org.onosproject.net.behaviour.DeviceSystemStatisticsQuery;
import org.onosproject.net.behaviour.DeviceSystemStats;
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.PortNumber;
import org.onosproject.protocol.rest.RestSBDevice;
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.Sets;
import com.google.common.collect.ImmutableList;
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.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import javax.ws.rs.ProcessingException;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.drivers.server.Constants.JSON;
import static org.onosproject.drivers.server.Constants.MSG_DEVICE_NULL;
import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
import static org.onosproject.drivers.server.Constants.MSG_NIC_NAME_NULL;
import static org.onosproject.drivers.server.Constants.MSG_NIC_PORT_NUMBER_NEGATIVE;
import static org.onosproject.drivers.server.Constants.MSG_STATS_TIMING_DEPLOY_INCONSISTENT;
import static org.onosproject.drivers.server.Constants.PARAM_ID;
import static org.onosproject.drivers.server.Constants.PARAM_CAPACITY;
import static org.onosproject.drivers.server.Constants.PARAM_CHASSIS_ID;
import static org.onosproject.drivers.server.Constants.PARAM_CPUS;
import static org.onosproject.drivers.server.Constants.PARAM_NICS;
import static org.onosproject.drivers.server.Constants.PARAM_NAME;
import static org.onosproject.drivers.server.Constants.PARAM_MEMORY;
import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_HIERARCHY;
import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_MODULES;
import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_STATS_FREE;
import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_STATS_TOTAL;
import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_STATS_USED;
import static org.onosproject.drivers.server.Constants.PARAM_MANUFACTURER;
import static org.onosproject.drivers.server.Constants.PARAM_HW_VENDOR;
import static org.onosproject.drivers.server.Constants.PARAM_SW_VENDOR;
import static org.onosproject.drivers.server.Constants.PARAM_SERIAL;
import static org.onosproject.drivers.server.Constants.PARAM_TIMING_STATS;
import static org.onosproject.drivers.server.Constants.PARAM_TIMING_STATS_AUTOSCALE;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_LEVEL;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_LEVELS;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_LINE_LEN;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_POLICY;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_SETS;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_SHARED;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_TYPE;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_WAYS;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHES;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_HIERARCHY;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_CORES;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_FREQUENCY;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_ID_LOG;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_ID_PHY;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_LOAD;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_LATENCY;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_QUEUE;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_SOCKET;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_SOCKETS;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_STATUS;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_THROUGHPUT;
import static org.onosproject.drivers.server.Constants.PARAM_CPU_VENDOR;
import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_WIDTH_DATA;
import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_WIDTH_TOTAL;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_HW_ADDR;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_PORT_TYPE;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_RX_FILTER;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_RX_COUNT;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_RX_BYTES;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_RX_DROPS;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_RX_ERRORS;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_TX_COUNT;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_TX_BYTES;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_TX_DROPS;
import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_TX_ERRORS;
import static org.onosproject.drivers.server.Constants.PARAM_MON_AVERAGE;
import static org.onosproject.drivers.server.Constants.PARAM_MON_BUSY_CPUS;
import static org.onosproject.drivers.server.Constants.PARAM_MON_FREE_CPUS;
import static org.onosproject.drivers.server.Constants.PARAM_MON_MAX;
import static org.onosproject.drivers.server.Constants.PARAM_MON_MIN;
import static org.onosproject.drivers.server.Constants.PARAM_MON_UNIT;
import static org.onosproject.drivers.server.Constants.PARAM_SPEED;
import static org.onosproject.drivers.server.Constants.PARAM_SPEED_CONF;
import static org.onosproject.drivers.server.Constants.PARAM_STATUS;
import static org.onosproject.drivers.server.Constants.PARAM_TIMING_PARSE;
import static org.onosproject.drivers.server.Constants.PARAM_TIMING_LAUNCH;
import static org.onosproject.drivers.server.Constants.PARAM_TIMING_DEPLOY;
import static org.onosproject.drivers.server.Constants.PARAM_TIMING_AUTOSCALE;
import static org.onosproject.drivers.server.Constants.PARAM_TYPE;
import static org.onosproject.drivers.server.Constants.SLASH;
import static org.onosproject.drivers.server.Constants.URL_SRV_GLOBAL_STATS;
import static org.onosproject.drivers.server.Constants.URL_SRV_RESOURCE_DISCOVERY;
import static org.onosproject.drivers.server.Constants.URL_SERVICE_CHAINS_STATS;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Discovers the device details of server devices.
*/
public class ServerDevicesDiscovery
extends BasicServerDriver
implements DevicesDiscovery, DeviceDescriptionDiscovery,
PortStatisticsDiscovery, CpuStatisticsDiscovery,
MonitoringStatisticsDiscovery, DeviceSystemStatisticsQuery {
private final Logger log = getLogger(getClass());
/**
* Auxiliary constants.
*/
private static final short DISCOVERY_RETRIES = 3;
/**
* Constructs server device discovery.
*/
public ServerDevicesDiscovery() {
super();
log.debug("Started");
}
/**
* Implements DevicesDiscovery behaviour.
*/
@Override
public Set<DeviceId> deviceIds() {
// Set of devices to return
Set<DeviceId> devices = new HashSet<DeviceId>();
DeviceId deviceId = getDeviceId();
checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
devices.add(deviceId);
return devices;
}
@Override
public DeviceDescription deviceDetails(DeviceId deviceId) {
return getDeviceDetails(deviceId);
}
/**
* Implements DeviceDescriptionDiscovery behaviour.
*/
@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 = getDeviceId();
checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
}
// Get the device
RestSBDevice device = getDevice(deviceId);
checkNotNull(device, MSG_DEVICE_NULL);
// Hit the path that provides the server's resources
InputStream response = null;
try {
response = getController().get(deviceId, URL_SRV_RESOURCE_DISCOVERY, 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, 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);
long chassisId = objNode.path(PARAM_CHASSIS_ID).asLong();
// Pass the southbound protocol as an annotation
DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
annotations.set(AnnotationKeys.PROTOCOL, "REST");
// Parse CPU devices
Collection<CpuDevice> cpuSet = parseCpuDevices(objNode);
// Parse memory hierarchy device
MemoryHierarchyDevice memHierarchyDev = parseMemoryHierarchyDevice(objNode);
// Parse CPU cache hierachy device
CpuCacheHierarchyDevice cacheHierarchyDev = parseCpuCacheHierarchyDevice(objNode);
// NICs are composite attributes too
Collection<NicDevice> nicSet = parseNicDevices(mapper, objNode, annotations);
// Construct a server device,
// i.e., a RestSBDevice extended with CPU, cache, memory, and NIC information
RestServerSBDevice dev = new DefaultRestServerSBDevice(
device.ip(), device.port(), device.username(),
device.password(), device.protocol(), device.url(),
device.isActive(), device.testUrl().orElse(""),
vendor, hw, sw, cpuSet, cacheHierarchyDev,
memHierarchyDev, nicSet);
checkNotNull(dev, MSG_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(chassisId),
cpuSet, cacheHierarchyDev, memHierarchyDev,
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;
}
/**
* Parse the input JSON object, looking for CPU-related
* information. Upon success, construct and return a list
* of CPU devices.
*
* @param objNode input JSON node with CPU device information
* @return list of CPU devices
*/
private Collection<CpuDevice> parseCpuDevices(ObjectNode objNode) {
Collection<CpuDevice> cpuSet = Sets.newHashSet();
JsonNode cpuNode = objNode.path(PARAM_CPUS);
// Construct CPU objects
for (JsonNode cn : cpuNode) {
ObjectNode cpuObjNode = (ObjectNode) cn;
// All the CPU attributes
int physicalCpuId = cpuObjNode.path(PARAM_CPU_ID_PHY).asInt();
int logicalCpuId = cpuObjNode.path(PARAM_CPU_ID_LOG).asInt();
int cpuSocket = cpuObjNode.path(PARAM_CPU_SOCKET).asInt();
String cpuVendorStr = get(cn, PARAM_CPU_VENDOR);
long cpuFrequency = cpuObjNode.path(PARAM_CPU_FREQUENCY).asLong();
// Construct a CPU device and add it to the set
cpuSet.add(
DefaultCpuDevice.builder()
.setCoreId(logicalCpuId, physicalCpuId)
.setVendor(cpuVendorStr)
.setSocket(cpuSocket)
.setFrequency(cpuFrequency)
.build());
}
return cpuSet;
}
/**
* Parse the input JSON object, looking for CPU cache-related
* information. Upon success, construct and return a CPU cache
* hierarchy device.
*
* @param objNode input JSON node with CPU cache device information
* @return a CPU cache hierarchy devices
*/
private CpuCacheHierarchyDevice parseCpuCacheHierarchyDevice(ObjectNode objNode) {
JsonNode cacheHierarchyNode = objNode.path(PARAM_CPU_CACHE_HIERARCHY);
ObjectNode cacheHierarchyObjNode = (ObjectNode) cacheHierarchyNode;
if (cacheHierarchyObjNode == null) {
return null;
}
int socketsNb = cacheHierarchyObjNode.path(PARAM_CPU_SOCKETS).asInt();
int coresNb = cacheHierarchyObjNode.path(PARAM_CPU_CORES).asInt();
int levels = cacheHierarchyObjNode.path(PARAM_CPU_CACHE_LEVELS).asInt();
JsonNode cacheNode = cacheHierarchyObjNode.path(PARAM_CPU_CACHES);
DefaultCpuCacheHierarchyDevice.Builder cacheBuilder =
DefaultCpuCacheHierarchyDevice.builder()
.setSocketsNumber(socketsNb)
.setCoresNumber(coresNb)
.setLevels(levels);
// Construct CPU cache objects
for (JsonNode cn : cacheNode) {
ObjectNode cacheObjNode = (ObjectNode) cn;
// CPU cache attributes
String cpuVendorStr = get(cn, PARAM_CPU_VENDOR);
String levelStr = get(cn, PARAM_CPU_CACHE_LEVEL);
String typeStr = get(cn, PARAM_CPU_CACHE_TYPE);
String policyStr = get(cn, PARAM_CPU_CACHE_POLICY);
long capacity = cacheObjNode.path(PARAM_CAPACITY).asLong();
int sets = cacheObjNode.path(PARAM_CPU_CACHE_SETS).asInt();
int ways = cacheObjNode.path(PARAM_CPU_CACHE_WAYS).asInt();
int lineLen = cacheObjNode.path(PARAM_CPU_CACHE_LINE_LEN).asInt();
boolean shared = cacheObjNode.path(PARAM_CPU_CACHE_SHARED).asInt() > 0;
// Construct a basic CPU cache device and add it to the hierarchy
cacheBuilder.addBasicCpuCacheDevice(
DefaultBasicCpuCacheDevice.builder()
.setVendor(cpuVendorStr)
.setCacheId(levelStr, typeStr)
.setPolicy(policyStr)
.setCapacity(capacity)
.setNumberOfSets(sets)
.setNumberOfWays(ways)
.setLineLength(lineLen)
.isShared(shared)
.build());
}
return cacheBuilder.build();
}
/**
* Parse the input JSON object, looking for memory-related
* information. Upon success, construct and return a memory
* hierarchy device.
*
* @param objNode input JSON node with memory device information
* @return a memory hierarchy device
*/
private MemoryHierarchyDevice parseMemoryHierarchyDevice(ObjectNode objNode) {
JsonNode memHierarchyNode = objNode.path(PARAM_MEMORY_HIERARCHY);
ObjectNode memoryHierarchyObjNode = (ObjectNode) memHierarchyNode;
if (memoryHierarchyObjNode == null) {
return null;
}
JsonNode memoryNode = memoryHierarchyObjNode.path(PARAM_MEMORY_MODULES);
DefaultMemoryHierarchyDevice.Builder memoryBuilder =
DefaultMemoryHierarchyDevice.builder();
// Construct memory modules
for (JsonNode mn : memoryNode) {
ObjectNode memoryObjNode = (ObjectNode) mn;
String typeStr = get(mn, PARAM_TYPE);
String manufacturerStr = get(mn, PARAM_MANUFACTURER);
String serialStr = get(mn, PARAM_SERIAL);
int dataWidth = memoryObjNode.path(PARAM_MEMORY_WIDTH_DATA).asInt();
int totalWidth = memoryObjNode.path(PARAM_MEMORY_WIDTH_TOTAL).asInt();
long capacity = memoryObjNode.path(PARAM_CAPACITY).asLong();
long speed = memoryObjNode.path(PARAM_SPEED).asLong();
long configuredSpeed = memoryObjNode.path(PARAM_SPEED_CONF).asLong();
// Construct a memory module and add it to the hierarchy
memoryBuilder.addMemoryModule(
DefaultMemoryModuleDevice.builder()
.setType(typeStr)
.setManufacturer(manufacturerStr)
.setSerialNumber(serialStr)
.setDataWidth(dataWidth)
.setTotalWidth(totalWidth)
.setCapacity(capacity)
.setSpeed(speed)
.setConfiguredSpeed(configuredSpeed)
.build());
}
return memoryBuilder.build();
}
/**
* Parse the input JSON object, looking for NIC-related
* information. Upon success, construct and return a list
* of NIC devices.
*
* @param mapper input JSON object mapper
* @param objNode input JSON node with NIC device information
* @param annotations device annotations
* @return list of CPU devices
*/
private Collection<NicDevice> parseNicDevices(
ObjectMapper mapper, ObjectNode objNode, DefaultAnnotations.Builder annotations) {
Collection<NicDevice> nicSet = Sets.newHashSet();
JsonNode nicNode = objNode.path(PARAM_NICS);
// Construct NIC objects
for (JsonNode nn : nicNode) {
ObjectNode nicObjNode = (ObjectNode) nn;
// All the NIC attributes
String nicName = get(nn, PARAM_NAME);
long nicIndex = nicObjNode.path(PARAM_ID).asLong();
long speed = nicObjNode.path(PARAM_SPEED).asLong();
String portTypeStr = get(nn, PARAM_NIC_PORT_TYPE);
boolean status = nicObjNode.path(PARAM_STATUS).asInt() > 0;
String hwAddr = get(nn, PARAM_NIC_HW_ADDR);
JsonNode tagNode = nicObjNode.path(PARAM_NIC_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;
}
// Store NIC name to number mapping as an annotation
annotations.set(nicName, Long.toString(nicIndex));
// Construct a NIC device and add it to the set
nicSet.add(
DefaultNicDevice.builder()
.setName(nicName)
.setPortNumber(nicIndex)
.setPortNumber(nicIndex)
.setPortType(portTypeStr)
.setSpeed(speed)
.setStatus(status)
.setMacAddress(hwAddr)
.setRxFilters(rxFilters)
.build());
}
return nicSet;
}
@Override
public List<PortDescription> discoverPortDetails() {
// Retrieve the device ID
DeviceId deviceId = getDeviceId();
checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
// .. and object
RestServerSBDevice device = null;
try {
device = (RestServerSBDevice) 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()
// CHECK: .withPortNumber(PortNumber.portNumber(nic.portNumber(), nic.name()))
.withPortNumber(PortNumber.portNumber(nic.portNumber()))
.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);
}
/**
* Implements PortStatisticsDiscovery behaviour.
*/
@Override
public Collection<PortStatistics> discoverPortStatistics() {
// Retrieve the device ID
DeviceId deviceId = getDeviceId();
checkNotNull(deviceId, MSG_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;
}
/**
* Implements CpuStatisticsDiscovery behaviour.
*/
@Override
public Collection<CpuStatistics> discoverCpuStatistics() {
// Retrieve the device ID
DeviceId deviceId = getDeviceId();
checkNotNull(deviceId, MSG_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;
}
/**
* Implements MonitoringStatisticsDiscovery behaviour.
*/
@Override
public MonitoringStatistics discoverGlobalMonitoringStatistics() {
// Retrieve the device ID
DeviceId deviceId = getDeviceId();
checkNotNull(deviceId, MSG_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) 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, URL_SRV_GLOBAL_STATS, 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(PARAM_MON_BUSY_CPUS).asInt();
int freeCpus = objNode.path(PARAM_MON_FREE_CPUS).asInt();
// Get a list of CPU statistics per core
Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
// Get main memory statistics
MemoryStatistics memStats = parseMemoryStatistics(deviceId, objNode);
// Get a list of port statistics
Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
// Get zero timing statistics
TimingStatistics timinsgStats = getZeroTimingStatistics();
// Construct a global monitoring statistics object out of smaller ones
monStats = DefaultMonitoringStatistics.builder()
.setDeviceId(deviceId)
.setTimingStatistics(timinsgStats)
.setCpuStatistics(cpuStats)
.setMemoryStatistics(memStats)
.setNicStatistics(nicStats)
.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 = getDeviceId();
checkNotNull(deviceId, MSG_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) 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 = URL_SERVICE_CHAINS_STATS + 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 main memory statistics
MemoryStatistics memStats = parseMemoryStatistics(deviceId, objNode);
// Get a list of port statistics
Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
// Get timing statistics
TimingStatistics timinsgStats = parseTimingStatistics(objNode);
// Construct a global monitoring statistics object out of smaller ones
monStats = DefaultMonitoringStatistics.builder()
.setDeviceId(deviceId)
.setTimingStatistics(timinsgStats)
.setCpuStatistics(cpuStats)
.setMemoryStatistics(memStats)
.setNicStatistics(nicStats)
.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(PARAM_CPUS);
for (JsonNode cn : cpuNode) {
ObjectNode cpuObjNode = (ObjectNode) cn;
// CPU statistics builder
DefaultCpuStatistics.Builder cpuStatsBuilder = DefaultCpuStatistics.builder();
// Throughput statistics are optional
JsonNode throughputNode = cpuObjNode.get(PARAM_CPU_THROUGHPUT);
if (throughputNode != null) {
String throughputUnit = get(throughputNode, PARAM_MON_UNIT);
if (!Strings.isNullOrEmpty(throughputUnit)) {
cpuStatsBuilder.setThroughputUnit(throughputUnit);
}
float averageThroughput = (float) 0;
if (throughputNode.get(PARAM_MON_AVERAGE) != null) {
averageThroughput = throughputNode.path(PARAM_MON_AVERAGE).floatValue();
}
cpuStatsBuilder.setAverageThroughput(averageThroughput);
}
// Latency statistics are optional
JsonNode latencyNode = cpuObjNode.get(PARAM_CPU_LATENCY);
if (latencyNode != null) {
String latencyUnit = get(latencyNode, PARAM_MON_UNIT);
if (!Strings.isNullOrEmpty(latencyUnit)) {
cpuStatsBuilder.setLatencyUnit(latencyUnit);
}
float minLatency = (float) 0;
if (latencyNode.get(PARAM_MON_MIN) != null) {
minLatency = latencyNode.path(PARAM_MON_MIN).floatValue();
}
float averageLatency = (float) 0;
if (latencyNode.get(PARAM_MON_AVERAGE) != null) {
averageLatency = latencyNode.path(PARAM_MON_AVERAGE).floatValue();
}
float maxLatency = (float) 0;
if (latencyNode.get(PARAM_MON_MAX) != null) {
maxLatency = latencyNode.path(PARAM_MON_MAX).floatValue();
}
cpuStatsBuilder.setMinLatency(minLatency)
.setAverageLatency(averageLatency)
.setMaxLatency(maxLatency);
}
// CPU ID with its load and status
int cpuId = cpuObjNode.path(PARAM_ID).asInt();
float cpuLoad = cpuObjNode.path(PARAM_CPU_LOAD).floatValue();
int queueId = cpuObjNode.path(PARAM_CPU_QUEUE).asInt();
int busySince = cpuObjNode.path(PARAM_CPU_STATUS).asInt();
// We have all the statistics for this CPU core
cpuStats.add(
cpuStatsBuilder
.setDeviceId(deviceId)
.setId(cpuId)
.setLoad(cpuLoad)
.setQueue(queueId)
.setBusySince(busySince)
.build());
}
return cpuStats;
}
/**
* Parse the input JSON object, looking for memory-related
* statistics. Upon success, construct and return a memory
* statistics objects.
*
* @param deviceId the device ID that sent the JSON object
* @param objNode input JSON node with memory statistics information
* @return memory statistics object
*/
private MemoryStatistics parseMemoryStatistics(DeviceId deviceId, JsonNode objNode) {
if ((deviceId == null) || (objNode == null)) {
return null;
}
JsonNode memoryNode = objNode.path(PARAM_MEMORY);
ObjectNode memoryObjNode = (ObjectNode) memoryNode;
// Fetch memory statistics
String unit = get(memoryNode, PARAM_MON_UNIT);
long used = memoryObjNode.path(PARAM_MEMORY_STATS_USED).asLong();
long free = memoryObjNode.path(PARAM_MEMORY_STATS_FREE).asLong();
long total = memoryObjNode.path(PARAM_MEMORY_STATS_TOTAL).asLong();
// Memory statistics builder
return DefaultMemoryStatistics.builder()
.setDeviceId(deviceId)
.setMemoryUsed(used)
.setMemoryFree(free)
.setMemoryTotal(total)
.build();
}
/**
* 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) 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, PARAM_NAME);
checkArgument(!Strings.isNullOrEmpty(nicName), MSG_NIC_NAME_NULL);
long portNumber = device.portNumberFromName(nicName);
checkArgument(portNumber >= 0, MSG_NIC_PORT_NUMBER_NEGATIVE);
long rxCount = nicObjNode.path(PARAM_NIC_STATS_RX_COUNT).asLong();
long rxBytes = nicObjNode.path(PARAM_NIC_STATS_RX_BYTES).asLong();
long rxDropped = nicObjNode.path(PARAM_NIC_STATS_RX_DROPS).asLong();
long rxErrors = nicObjNode.path(PARAM_NIC_STATS_RX_ERRORS).asLong();
long txCount = nicObjNode.path(PARAM_NIC_STATS_TX_COUNT).asLong();
long txBytes = nicObjNode.path(PARAM_NIC_STATS_TX_BYTES).asLong();
long txDropped = nicObjNode.path(PARAM_NIC_STATS_TX_DROPS).asLong();
long txErrors = nicObjNode.path(PARAM_NIC_STATS_TX_ERRORS).asLong();
// Construct a NIC statistics object and add it to the set
nicStats.add(
DefaultPortStatistics.builder()
.setDeviceId(deviceId)
.setPort(PortNumber.portNumber(portNumber))
.setPacketsReceived(rxCount)
.setPacketsSent(txCount)
.setBytesReceived(rxBytes)
.setBytesSent(txBytes)
.setPacketsRxDropped(rxDropped)
.setPacketsRxErrors(rxErrors)
.setPacketsTxDropped(txDropped)
.setPacketsTxErrors(txErrors)
.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, PARAM_MON_UNIT);
if (!Strings.isNullOrEmpty(timingStatsUnit)) {
timingBuilder.setUnit(timingStatsUnit);
}
// Time (ns) to parse the controller's deployment instruction
long parsingTime = 0;
if (timingObjNode.get(PARAM_TIMING_PARSE) != null) {
parsingTime = timingObjNode.path(PARAM_TIMING_PARSE).asLong();
}
// Time (ns) to do the deployment
long launchingTime = 0;
if (timingObjNode.get(PARAM_TIMING_LAUNCH) != null) {
launchingTime = timingObjNode.path(PARAM_TIMING_LAUNCH).asLong();
}
// Deployment time (ns) equals to time to parse + time to launch
long deployTime = 0;
if (timingObjNode.get(PARAM_TIMING_DEPLOY) != null) {
deployTime = timingObjNode.path(PARAM_TIMING_DEPLOY).asLong();
}
checkArgument(deployTime == parsingTime + launchingTime,
MSG_STATS_TIMING_DEPLOY_INCONSISTENT);
timingBuilder.setParsingTime(parsingTime)
.setLaunchingTime(launchingTime);
// Get autoscale timing statistics
JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_STATS_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(PARAM_TIMING_AUTOSCALE) != null) {
autoScaleTime = autoScaleTimingObjNode.path(PARAM_TIMING_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() {
return DefaultTimingStatistics.builder()
.setParsingTime(0)
.setLaunchingTime(0)
.setAutoScaleTime(0)
.build();
}
/**
* Implements DeviceSystemStatisticsQuery behaviour.
*/
@Override
public Optional<DeviceSystemStats> getDeviceSystemStats() {
// Retrieve the device ID from the handler
DeviceId deviceId = getDeviceId();
checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
// ....to retrieve monitoring statistics
MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
Optional<DeviceCpuStats> cpuStats = getOverallCpuUsage(monStats);
Optional<DeviceMemoryStats> memoryStats = getOverallMemoryUsage(monStats);
if (cpuStats.isPresent() && memoryStats.isPresent()) {
return Optional.of(new DeviceSystemStats(memoryStats.get(), cpuStats.get()));
} else {
return Optional.empty();
}
}
/**
* Get CPU usage of server device.
*
* @param monStats global monitoring statistics which contain CPU statistics
* @return cpuStats, device CPU usage stats if available
*/
private Optional<DeviceCpuStats> getOverallCpuUsage(MonitoringStatistics monStats) {
if (monStats == null) {
return Optional.empty();
}
if (monStats.numberOfCpus() == 0) {
return Optional.of(new DeviceCpuStats());
}
float usedCpu = 0.0f;
for (CpuStatistics cpuCoreStats : monStats.cpuStatisticsAll()) {
if (cpuCoreStats.busy()) {
usedCpu += cpuCoreStats.load();
}
}
return Optional.of(new DeviceCpuStats(usedCpu / (float) monStats.numberOfCpus()));
}
/**
* Get memory usage of server device in KB.
*
* @param monStats global monitoring statistics which contain memory statistics
* @return memoryStats, device memory usage stats if available
*/
private Optional<DeviceMemoryStats> getOverallMemoryUsage(MonitoringStatistics monStats) {
if (monStats == null) {
return Optional.empty();
}
MemoryStatistics memStats = monStats.memoryStatistics();
return Optional.of(
new DeviceMemoryStats(memStats.free(), memStats.used(), memStats.total()));
}
}