blob: 10203a33e2c23024c6325bc0cca3c9677bde045c [file] [log] [blame]
Georgios Katsikas83600982017-05-28 20:41:45 +02001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.drivers.server;
18
19import org.onosproject.drivers.server.behavior.CpuStatisticsDiscovery;
20import org.onosproject.drivers.server.behavior.MonitoringStatisticsDiscovery;
21import org.onosproject.drivers.server.devices.CpuDevice;
22import org.onosproject.drivers.server.devices.CpuVendor;
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +020023import org.onosproject.drivers.server.devices.nic.NicDevice;
24import org.onosproject.drivers.server.devices.nic.NicRxFilter;
25import org.onosproject.drivers.server.devices.nic.NicRxFilter.RxFilter;
Georgios Katsikas83600982017-05-28 20:41:45 +020026import org.onosproject.drivers.server.devices.ServerDeviceDescription;
27import org.onosproject.drivers.server.devices.RestServerSBDevice;
28import org.onosproject.drivers.server.stats.CpuStatistics;
29import org.onosproject.drivers.server.stats.MonitoringStatistics;
30import org.onosproject.drivers.server.stats.TimingStatistics;
31
32import org.onosproject.drivers.server.impl.devices.DefaultCpuDevice;
33import org.onosproject.drivers.server.impl.devices.DefaultNicDevice;
34import org.onosproject.drivers.server.impl.devices.DefaultRestServerSBDevice;
35import org.onosproject.drivers.server.impl.devices.DefaultServerDeviceDescription;
36import org.onosproject.drivers.server.impl.stats.DefaultCpuStatistics;
37import org.onosproject.drivers.server.impl.stats.DefaultMonitoringStatistics;
38import org.onosproject.drivers.server.impl.stats.DefaultTimingStatistics;
39
40import org.onlab.packet.ChassisId;
41import org.onosproject.net.AnnotationKeys;
42import org.onosproject.net.Device;
43import org.onosproject.net.DeviceId;
44import org.onosproject.net.DefaultAnnotations;
45import org.onosproject.net.behaviour.DevicesDiscovery;
46import org.onosproject.net.device.DeviceDescription;
47import org.onosproject.net.device.DeviceDescriptionDiscovery;
48import org.onosproject.net.device.DefaultPortStatistics;
49import org.onosproject.net.device.DefaultPortDescription;
50import org.onosproject.net.device.PortDescription;
51import org.onosproject.net.device.PortStatistics;
52import org.onosproject.net.device.PortStatisticsDiscovery;
53import org.onosproject.net.Port;
54import org.onosproject.net.PortNumber;
55import org.onosproject.protocol.rest.RestSBDevice;
56import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
57
58import org.slf4j.Logger;
59
60import com.fasterxml.jackson.core.type.TypeReference;
61import com.fasterxml.jackson.databind.JsonNode;
62import com.fasterxml.jackson.databind.ObjectMapper;
63import com.fasterxml.jackson.databind.node.ObjectNode;
64
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +020065import com.google.common.base.Strings;
Georgios Katsikas83600982017-05-28 20:41:45 +020066import com.google.common.collect.Lists;
67import com.google.common.collect.ImmutableList;
68
69import javax.ws.rs.ProcessingException;
70import java.io.InputStream;
71import java.io.IOException;
72import java.net.URI;
73import java.net.URISyntaxException;
74import java.util.ArrayList;
75import java.util.Collection;
76import java.util.Collections;
77import java.util.HashSet;
78import java.util.HashMap;
79import java.util.List;
80import java.util.Map;
81import java.util.Set;
82import java.util.TreeSet;
83
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +020084import static com.google.common.base.Preconditions.checkArgument;
Georgios Katsikas83600982017-05-28 20:41:45 +020085import static com.google.common.base.Preconditions.checkNotNull;
86import static org.slf4j.LoggerFactory.getLogger;
87
88/**
89 * Discovers the device details of
90 * REST-based commodity server devices.
91 */
92public class ServerDevicesDiscovery extends BasicServerDriver
93 implements DevicesDiscovery, DeviceDescriptionDiscovery,
94 PortStatisticsDiscovery, CpuStatisticsDiscovery,
95 MonitoringStatisticsDiscovery {
96
97 private final Logger log = getLogger(getClass());
98
99 /**
100 * Resource endpoints of the server agent (REST server-side).
101 */
Georgios Katsikas30bede52018-07-28 14:46:07 +0200102 private static final String RESOURCE_DISCOVERY_URL = BASE_URL + SLASH + "resources";
103 private static final String GLOBAL_STATS_URL = BASE_URL + SLASH + "stats";
104 private static final String SERVICE_CHAINS_STATS_URL = BASE_URL + SLASH + "chains_stats"; // + /ID
Georgios Katsikas83600982017-05-28 20:41:45 +0200105
106 /**
107 * Parameters to be exchanged with the server's agent.
108 */
Georgios Katsikas83600982017-05-28 20:41:45 +0200109 private static final String PARAM_MANUFACTURER = "manufacturer";
110 private static final String PARAM_HW_VENDOR = "hwVersion";
111 private static final String PARAM_SW_VENDOR = "swVersion";
112 private static final String PARAM_SERIAL = "serial";
Georgios Katsikas83600982017-05-28 20:41:45 +0200113 private static final String PARAM_TIMING_STATS = "timing_stats";
114 private static final String PARAM_TIMING_AUTOSCALE = "autoscale_timing_stats";
115
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200116 private static final String NIC_PARAM_NAME = "name";
117 private static final String NIC_PARAM_PORT_INDEX = "index";
Georgios Katsikas83600982017-05-28 20:41:45 +0200118 private static final String NIC_PARAM_PORT_TYPE = "portType";
119 private static final String NIC_PARAM_PORT_TYPE_FIBER = "fiber";
120 private static final String NIC_PARAM_PORT_TYPE_COPPER = "copper";
121 private static final String NIC_PARAM_SPEED = "speed";
122 private static final String NIC_PARAM_STATUS = "status";
123 private static final String NIC_PARAM_HW_ADDR = "hwAddr";
Georgios Katsikas83600982017-05-28 20:41:45 +0200124
125 /**
126 * NIC statistics.
127 */
128 private static final String NIC_STATS_TX_COUNT = "txCount";
129 private static final String NIC_STATS_TX_BYTES = "txBytes";
130 private static final String NIC_STATS_TX_DROPS = "txDropped";
131 private static final String NIC_STATS_TX_ERRORS = "txErrors";
132 private static final String NIC_STATS_RX_COUNT = "rxCount";
133 private static final String NIC_STATS_RX_BYTES = "rxBytes";
134 private static final String NIC_STATS_RX_DROPS = "rxDropped";
135 private static final String NIC_STATS_RX_ERRORS = "rxErrors";
136
137 /**
138 * CPU statistics.
139 */
140 private static final String CPU_PARAM_ID = "id";
141 private static final String CPU_PARAM_VENDOR = "vendor";
142 private static final String CPU_PARAM_FREQUENCY = "frequency";
143 private static final String CPU_PARAM_LOAD = "load";
144 private static final String CPU_PARAM_STATUS = "busy";
145 private static final String CPU_STATS_BUSY_CPUS = "busyCpus";
146 private static final String CPU_STATS_FREE_CPUS = "freeCpus";
147
148 /**
149 * Timing statistics.
150 */
151 private static final String TIMING_PARAM_PARSE = "parse";
152 private static final String TIMING_PARAM_LAUNCH = "launch";
153 private static final String TIMING_PARAM_AUTOSCALE = "autoscale";
154
155 /**
156 * Auxiliary constants.
157 */
158 private static final short DISCOVERY_RETRIES = 3;
159 private static final String CPU_VENDOR_NULL = "Unsupported CPU vendor" +
160 " Choose one in: " + BasicServerDriver.enumTypesToString(CpuVendor.class);
161 private static final String NIC_RX_FILTER_NULL = "Unsupported NIC Rx filter" +
162 " Choose one in: " + BasicServerDriver.enumTypesToString(RxFilter.class);
163
164 /**
165 * Port types that usually appear in commodity servers.
166 */
167 public static final Map<String, Port.Type> PORT_TYPE_MAP =
168 Collections.unmodifiableMap(
169 new HashMap<String, Port.Type>() {
170 {
171 put(NIC_PARAM_PORT_TYPE_FIBER, Port.Type.FIBER);
172 put(NIC_PARAM_PORT_TYPE_COPPER, Port.Type.COPPER);
173 }
174 }
175 );
176
177 /**
178 * Constructs server device discovery.
179 */
180 public ServerDevicesDiscovery() {
181 super();
182 log.debug("Started");
183 }
184
185 @Override
186 public Set<DeviceId> deviceIds() {
187 // Set of devices to return
188 Set<DeviceId> devices = new HashSet<DeviceId>();
189
190 DeviceId deviceId = getHandler().data().deviceId();
191 checkNotNull(deviceId, DEVICE_ID_NULL);
192 devices.add(deviceId);
193
194 return devices;
195 }
196
197 @Override
198 public DeviceDescription deviceDetails(DeviceId deviceId) {
199 return getDeviceDetails(deviceId);
200 }
201
202 @Override
203 public DeviceDescription discoverDeviceDetails() {
204 return getDeviceDetails(null);
205 }
206
207 /**
208 * Query a server to retrieve its features.
209 *
210 * @param deviceId the device ID to be queried
211 * @return a DeviceDescription with the device's features
212 */
213 private DeviceDescription getDeviceDetails(DeviceId deviceId) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200214 // Retrieve the device ID, if null given
215 if (deviceId == null) {
216 deviceId = getHandler().data().deviceId();
217 checkNotNull(deviceId, DEVICE_ID_NULL);
218 }
219
220 // Get the device
221 RestSBDevice device = getController().getDevice(deviceId);
222 checkNotNull(device, DEVICE_NULL);
223
224 // Hit the path that provides the server's resources
225 InputStream response = null;
226 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200227 response = getController().get(deviceId, RESOURCE_DISCOVERY_URL, JSON);
Georgios Katsikas83600982017-05-28 20:41:45 +0200228 } catch (ProcessingException pEx) {
229 log.error("Failed to discover the device details of: {}", deviceId);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200230 return null;
Georgios Katsikas83600982017-05-28 20:41:45 +0200231 }
232
233 // Load the JSON into objects
234 ObjectMapper mapper = new ObjectMapper();
235 Map<String, Object> jsonMap = null;
236 JsonNode jsonNode = null;
237 ObjectNode objNode = null;
238 try {
239 jsonMap = mapper.readValue(response, Map.class);
240 jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
241 objNode = (ObjectNode) jsonNode;
242 } catch (IOException ioEx) {
243 log.error("Failed to discover the device details of: {}", deviceId);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200244 return null;
Georgios Katsikas83600982017-05-28 20:41:45 +0200245 }
246
247 if (jsonMap == null) {
248 log.error("Failed to discover the device details of: {}", deviceId);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200249 return null;
Georgios Katsikas83600982017-05-28 20:41:45 +0200250 }
251
252 // Get all the attributes
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200253 String id = get(jsonNode, BasicServerDriver.PARAM_ID);
Georgios Katsikas83600982017-05-28 20:41:45 +0200254 String vendor = get(jsonNode, PARAM_MANUFACTURER);
255 String hw = get(jsonNode, PARAM_HW_VENDOR);
256 String sw = get(jsonNode, PARAM_SW_VENDOR);
257 String serial = get(jsonNode, PARAM_SERIAL);
258
259 // CPUs are composite attributes
260 Set<CpuDevice> cpuSet = new HashSet<CpuDevice>();
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200261 JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
Georgios Katsikas83600982017-05-28 20:41:45 +0200262
263 // Construct CPU objects
264 for (JsonNode cn : cpuNode) {
265 ObjectNode cpuObjNode = (ObjectNode) cn;
266
267 // All the CPU attributes
268 int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
269 String cpuVendorStr = get(cn, CPU_PARAM_VENDOR);
270 long cpuFrequency = cpuObjNode.path(CPU_PARAM_FREQUENCY).asLong();
271
272 // Verify that this is a valid vendor
273 CpuVendor cpuVendor = CpuVendor.getByName(cpuVendorStr);
274 checkNotNull(cpuVendor, CPU_VENDOR_NULL);
275
276 // Construct a CPU device
277 CpuDevice cpu = new DefaultCpuDevice(cpuId, cpuVendor, cpuFrequency);
278
279 // Add it to the set
280 cpuSet.add(cpu);
281 }
282
283 // NICs are composite attributes too
284 Set<NicDevice> nicSet = new HashSet<NicDevice>();
285 JsonNode nicNode = objNode.path(PARAM_NICS);
286
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200287 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
288
Georgios Katsikas83600982017-05-28 20:41:45 +0200289 // Construct NIC objects
290 for (JsonNode nn : nicNode) {
291 ObjectNode nicObjNode = (ObjectNode) nn;
292
293 // All the NIC attributes
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200294 String nicName = get(nn, NIC_PARAM_NAME);
295 long nicIndex = nicObjNode.path(NIC_PARAM_PORT_INDEX).asLong();
Georgios Katsikas83600982017-05-28 20:41:45 +0200296 long speed = nicObjNode.path(NIC_PARAM_SPEED).asLong();
297 String portTypeStr = get(nn, NIC_PARAM_PORT_TYPE);
298 Port.Type portType = PORT_TYPE_MAP.get(portTypeStr);
299 if (portType == null) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800300 throw new IllegalArgumentException(
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200301 portTypeStr + " is not a valid port type for NIC " + nicName);
Georgios Katsikas83600982017-05-28 20:41:45 +0200302 }
303 boolean status = nicObjNode.path(NIC_PARAM_STATUS).asInt() > 0;
304 String hwAddr = get(nn, NIC_PARAM_HW_ADDR);
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200305 JsonNode tagNode = nicObjNode.path(BasicServerDriver.NIC_PARAM_RX_FILTER);
Georgios Katsikas83600982017-05-28 20:41:45 +0200306 if (tagNode == null) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800307 throw new IllegalArgumentException(
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200308 "The Rx filters of NIC " + nicName + " are not reported");
Georgios Katsikas83600982017-05-28 20:41:45 +0200309 }
310
311 // Convert the JSON list into an array of strings
312 List<String> rxFilters = null;
313 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200314 rxFilters = mapper.readValue(tagNode.traverse(),
315 new TypeReference<ArrayList<String>>() { });
Georgios Katsikas83600982017-05-28 20:41:45 +0200316 } catch (IOException ioEx) {
317 continue;
318 }
319
320 // Parse the array of strings and create an RxFilter object
321 NicRxFilter rxFilterMechanism = new NicRxFilter();
322 for (String s : rxFilters) {
323 // Verify that this is a valid Rx filter
324 RxFilter rf = RxFilter.getByName(s);
325 checkNotNull(rf, NIC_RX_FILTER_NULL);
326
327 rxFilterMechanism.addRxFilter(rf);
328 }
329
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200330 // Store NIC name to number mapping as an annotation
331 annotations.set(nicName, Long.toString(nicIndex));
332
Georgios Katsikas83600982017-05-28 20:41:45 +0200333 // Construct a NIC device for this server
334 NicDevice nic = new DefaultNicDevice(
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200335 nicName, nicIndex, portType, speed, status, hwAddr, rxFilterMechanism);
Georgios Katsikas83600982017-05-28 20:41:45 +0200336
337 // Add it to the set
338 nicSet.add(nic);
339 }
340
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200341 // Construct a complete server device object.
342 // Lists of NICs and CPUs extend the information
343 // already in RestSBDevice (parent class).
Georgios Katsikas83600982017-05-28 20:41:45 +0200344 RestServerSBDevice dev = new DefaultRestServerSBDevice(
345 device.ip(), device.port(), device.username(),
346 device.password(), device.protocol(), device.url(),
347 device.isActive(), device.testUrl().toString(),
348 vendor, hw, sw, AuthenticationScheme.BASIC, "",
349 cpuSet, nicSet
350 );
351 checkNotNull(dev, DEVICE_NULL);
352
353 // Updates the controller with the complete device information
354 getController().removeDevice(deviceId);
355 getController().addDevice((RestSBDevice) dev);
356
Georgios Katsikas30bede52018-07-28 14:46:07 +0200357 // Create a description for this server device
358 ServerDeviceDescription desc = null;
359
Georgios Katsikas83600982017-05-28 20:41:45 +0200360 try {
361 desc = new DefaultServerDeviceDescription(
Georgios Katsikas40ecef32018-07-03 11:17:44 +0200362 new URI(id), Device.Type.SERVER, vendor,
Georgios Katsikas83600982017-05-28 20:41:45 +0200363 hw, sw, serial, new ChassisId(),
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200364 cpuSet, nicSet, annotations.build()
Georgios Katsikas83600982017-05-28 20:41:45 +0200365 );
366 } catch (URISyntaxException uEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200367 log.error("Failed to create a server device description for: {}",
368 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200369 return null;
370 }
371
372 log.info("Device's {} details sent to the controller", deviceId);
373
374 return desc;
375 }
376
377 @Override
378 public List<PortDescription> discoverPortDetails() {
Georgios Katsikas83600982017-05-28 20:41:45 +0200379 // Retrieve the device ID
380 DeviceId deviceId = getHandler().data().deviceId();
381 checkNotNull(deviceId, DEVICE_ID_NULL);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200382
Georgios Katsikas83600982017-05-28 20:41:45 +0200383 // .. and object
384 RestServerSBDevice device = null;
Georgios Katsikas30bede52018-07-28 14:46:07 +0200385 try {
386 device = (RestServerSBDevice) getController().getDevice(deviceId);
387 } catch (ClassCastException ccEx) {
388 log.error("Failed to discover ports for device {}", deviceId);
389 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200390 }
391
Georgios Katsikas30bede52018-07-28 14:46:07 +0200392 if (device == null) {
393 log.error("No device with ID {} is available for port discovery", deviceId);
394 return Collections.EMPTY_LIST;
395 }
396 if ((device.nics() == null) || (device.nics().size() == 0)) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200397 log.error("No ports available on {}", deviceId);
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200398 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200399 }
400
Georgios Katsikas30bede52018-07-28 14:46:07 +0200401 // List of port descriptions to return
402 List<PortDescription> portDescriptions = Lists.newArrayList();
403
Georgios Katsikas83600982017-05-28 20:41:45 +0200404 // Sorted list of NIC ports
405 Set<NicDevice> nics = new TreeSet(device.nics());
406
407 // Iterate through the NICs of this device to populate the list
Georgios Katsikas83600982017-05-28 20:41:45 +0200408 for (NicDevice nic : nics) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200409 // Include the name of this device as an annotation
410 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200411 .set(AnnotationKeys.PORT_NAME, nic.name());
Georgios Katsikas83600982017-05-28 20:41:45 +0200412
413 // Create a port description and add it to the list
414 portDescriptions.add(
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800415 DefaultPortDescription.builder()
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200416 .withPortNumber(PortNumber.portNumber(nic.portNumber(), nic.name()))
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800417 .isEnabled(nic.status())
418 .type(nic.portType())
419 .portSpeed(nic.speed())
420 .annotations(annotations.build())
421 .build());
Georgios Katsikas83600982017-05-28 20:41:45 +0200422
Georgios Katsikasd472a322018-07-08 19:58:25 +0200423 log.info("Port discovery on device {}: NIC {} is {} at {} Mbps",
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200424 deviceId, nic.portNumber(), nic.status() ? "up" : "down",
Georgios Katsikasd472a322018-07-08 19:58:25 +0200425 nic.speed());
Georgios Katsikas83600982017-05-28 20:41:45 +0200426 }
427
428 return ImmutableList.copyOf(portDescriptions);
429 }
430
431 @Override
432 public Collection<PortStatistics> discoverPortStatistics() {
433 // Retrieve the device ID
434 DeviceId deviceId = getHandler().data().deviceId();
435 checkNotNull(deviceId, DEVICE_ID_NULL);
436
437 // Get port statistics for this device
438 return getPortStatistics(deviceId);
439 }
440
441 /**
442 * Query a server to retrieve its port statistics.
443 *
444 * @param deviceId the device ID to be queried
445 * @return list of (per port) PortStatistics
446 */
447 private Collection<PortStatistics> getPortStatistics(DeviceId deviceId) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200448 // Get global monitoring statistics
449 MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
450 if (monStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200451 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200452 }
453
454 // Filter out the NIC statistics
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200455 Collection<PortStatistics> portStats = monStats.nicStatisticsAll();
Georgios Katsikas83600982017-05-28 20:41:45 +0200456 if (portStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200457 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200458 }
459
460 log.debug("Port statistics: {}", portStats.toString());
461
462 return portStats;
463 }
464
465 @Override
466 public Collection<CpuStatistics> discoverCpuStatistics() {
467 // Retrieve the device ID
468 DeviceId deviceId = getHandler().data().deviceId();
469 checkNotNull(deviceId, DEVICE_ID_NULL);
470
471 // Get CPU statistics for this device
472 return getCpuStatistics(deviceId);
473 }
474
475 /**
476 * Query a server to retrieve its CPU statistics.
477 *
478 * @param deviceId the device ID to be queried
479 * @return list of (per core) CpuStatistics
480 */
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200481 public Collection<CpuStatistics> getCpuStatistics(DeviceId deviceId) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200482 // Get global monitoring statistics
483 MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
484 if (monStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200485 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200486 }
487
488 // Filter out the CPU statistics
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200489 Collection<CpuStatistics> cpuStats = monStats.cpuStatisticsAll();
Georgios Katsikas83600982017-05-28 20:41:45 +0200490 if (cpuStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200491 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200492 }
493
494 log.debug("CPU statistics: {}", cpuStats.toString());
495
496 return cpuStats;
497 }
498
499 @Override
500 public MonitoringStatistics discoverGlobalMonitoringStatistics() {
501 // Retrieve the device ID
502 DeviceId deviceId = getHandler().data().deviceId();
503 checkNotNull(deviceId, DEVICE_ID_NULL);
504
505 // Get global monitoring statistics for this device
506 return getGlobalMonitoringStatistics(deviceId);
507 }
508
509 /**
510 * Query a server to retrieve its global monitoring statistics.
511 *
512 * @param deviceId the device ID to be queried
513 * @return global monitoring statistics
514 */
515 private MonitoringStatistics getGlobalMonitoringStatistics(DeviceId deviceId) {
516 // Monitoring statistics to return
517 MonitoringStatistics monStats = null;
518
519 RestServerSBDevice device = null;
520 try {
521 device = (RestServerSBDevice) getController().getDevice(deviceId);
522 } catch (ClassCastException ccEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200523 log.error("Failed to retrieve global monitoring statistics from device {}",
524 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200525 return monStats;
526 }
Georgios Katsikas30bede52018-07-28 14:46:07 +0200527 if ((device == null) || (!device.isActive())) {
528 return monStats;
529 }
Georgios Katsikas83600982017-05-28 20:41:45 +0200530
531 // Hit the path that provides the server's global resources
532 InputStream response = null;
533 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200534 response = getController().get(deviceId, GLOBAL_STATS_URL, JSON);
Georgios Katsikas83600982017-05-28 20:41:45 +0200535 } catch (ProcessingException pEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200536 log.error("Failed to retrieve global monitoring statistics from device {}",
537 deviceId);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200538 raiseDeviceDisconnect(device);
Georgios Katsikas83600982017-05-28 20:41:45 +0200539 return monStats;
540 }
541
542 // Load the JSON into objects
543 ObjectMapper mapper = new ObjectMapper();
544 Map<String, Object> jsonMap = null;
Georgios Katsikas83600982017-05-28 20:41:45 +0200545 ObjectNode objNode = null;
546 try {
547 jsonMap = mapper.readValue(response, Map.class);
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200548 JsonNode jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
Georgios Katsikas83600982017-05-28 20:41:45 +0200549 objNode = (ObjectNode) jsonNode;
550 } catch (IOException ioEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200551 log.error("Failed to retrieve global monitoring statistics from device {}",
552 deviceId);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200553 raiseDeviceDisconnect(device);
Georgios Katsikas83600982017-05-28 20:41:45 +0200554 return monStats;
555 }
556
557 if (jsonMap == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200558 log.error("Failed to retrieve global monitoring statistics from device {}",
559 deviceId);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200560 raiseDeviceDisconnect(device);
Georgios Katsikas83600982017-05-28 20:41:45 +0200561 return monStats;
562 }
563
564 // Get high-level CPU statistics
565 int busyCpus = objNode.path(CPU_STATS_BUSY_CPUS).asInt();
566 int freeCpus = objNode.path(CPU_STATS_FREE_CPUS).asInt();
567
568 // Get a list of CPU statistics per core
569 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
570
571 // Get a list of port statistics
572 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
573
574 // Get zero timing statistics
575 TimingStatistics timinsgStats = getZeroTimingStatistics();
576
577 // Ready to construct the grand object
578 DefaultMonitoringStatistics.Builder statsBuilder =
579 DefaultMonitoringStatistics.builder();
580
581 statsBuilder.setDeviceId(deviceId)
582 .setTimingStatistics(timinsgStats)
583 .setCpuStatistics(cpuStats)
584 .setNicStatistics(nicStats)
585 .build();
586
587 monStats = statsBuilder.build();
588
589 log.debug("Global monitoring statistics: {}", monStats.toString());
590
591 return monStats;
592 }
593
594 @Override
595 public MonitoringStatistics discoverMonitoringStatistics(URI tcId) {
596 // Retrieve the device ID
597 DeviceId deviceId = getHandler().data().deviceId();
598 checkNotNull(deviceId, DEVICE_ID_NULL);
599
600 // Get resource-specific monitoring statistics for this device
601 return getMonitoringStatistics(deviceId, tcId);
602 }
603
604 /**
605 * Query a server to retrieve monitoring statistics for a
606 * specific resource (i.e., traffic class).
607 *
608 * @param deviceId the device ID to be queried
609 * @param tcId the ID of the traffic class to be monitored
610 * @return resource-specific monitoring statistics
611 */
612 private MonitoringStatistics getMonitoringStatistics(DeviceId deviceId, URI tcId) {
613 // Monitoring statistics to return
614 MonitoringStatistics monStats = null;
615
616 RestServerSBDevice device = null;
617 try {
618 device = (RestServerSBDevice) getController().getDevice(deviceId);
619 } catch (ClassCastException ccEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200620 log.error("Failed to retrieve monitoring statistics from device {}",
621 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200622 return monStats;
623 }
Georgios Katsikas30bede52018-07-28 14:46:07 +0200624 if (device == null) {
625 return monStats;
626 }
Georgios Katsikas83600982017-05-28 20:41:45 +0200627
628 // Create a resource-specific URL
Georgios Katsikas30bede52018-07-28 14:46:07 +0200629 String scUrl = SERVICE_CHAINS_STATS_URL + SLASH + tcId.toString();
Georgios Katsikas83600982017-05-28 20:41:45 +0200630
631 // Hit the path that provides the server's specific resources
632 InputStream response = null;
633 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200634 response = getController().get(deviceId, scUrl, JSON);
Georgios Katsikas83600982017-05-28 20:41:45 +0200635 } catch (ProcessingException pEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200636 log.error("Failed to retrieve monitoring statistics from device {}",
637 deviceId);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200638 raiseDeviceDisconnect(device);
Georgios Katsikas83600982017-05-28 20:41:45 +0200639 return monStats;
640 }
641
642 // Load the JSON into objects
643 ObjectMapper mapper = new ObjectMapper();
644 Map<String, Object> jsonMap = null;
645 JsonNode jsonNode = null;
646 ObjectNode objNode = null;
647 try {
648 jsonMap = mapper.readValue(response, Map.class);
649 jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
650 objNode = (ObjectNode) jsonNode;
651 } catch (IOException ioEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200652 log.error("Failed to retrieve monitoring statistics from device {}",
653 deviceId);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200654 raiseDeviceDisconnect(device);
Georgios Katsikas83600982017-05-28 20:41:45 +0200655 return monStats;
656 }
657
658 if (jsonMap == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200659 log.error("Failed to retrieve monitoring statistics from device {}",
660 deviceId);
Georgios Katsikas30bede52018-07-28 14:46:07 +0200661 raiseDeviceDisconnect(device);
Georgios Katsikas83600982017-05-28 20:41:45 +0200662 return monStats;
663 }
664
665 // Get the ID of the traffic class
666 String id = get(jsonNode, PARAM_ID);
667
668 // And verify that this is the traffic class we want to monitor
669 if (!id.equals(tcId.toString())) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800670 throw new IllegalStateException(
Georgios Katsikas83600982017-05-28 20:41:45 +0200671 "Failed to retrieve monitoring data for traffic class " + tcId +
672 ". Traffic class ID does not agree."
673 );
674 }
675
676 // Get a list of CPU statistics per core
677 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
678
679 // Get a list of port statistics
680 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
681
682 // Get timing statistics
683 TimingStatistics timinsgStats = parseTimingStatistics(objNode);
684
685 // Ready to construct the grand object
686 DefaultMonitoringStatistics.Builder statsBuilder =
687 DefaultMonitoringStatistics.builder();
688
689 statsBuilder.setDeviceId(deviceId)
690 .setTimingStatistics(timinsgStats)
691 .setCpuStatistics(cpuStats)
692 .setNicStatistics(nicStats)
693 .build();
694
695 monStats = statsBuilder.build();
696
697 log.debug("Monitoring statistics: {}", monStats.toString());
698
699 return monStats;
700 }
701
702 /**
703 * Parse the input JSON object, looking for CPU-related
704 * statistics. Upon success, construct and return a list
705 * of CPU statistics objects.
706 *
707 * @param deviceId the device ID that sent the JSON object
708 * @param objNode input JSON node with CPU statistics information
709 * @return list of (per core) CpuStatistics
710 */
Georgios Katsikas30bede52018-07-28 14:46:07 +0200711 private Collection<CpuStatistics> parseCpuStatistics(DeviceId deviceId, JsonNode objNode) {
712 if ((deviceId == null) || (objNode == null)) {
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200713 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200714 }
715
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200716 Collection<CpuStatistics> cpuStats = Lists.newArrayList();
717
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200718 JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
Georgios Katsikas83600982017-05-28 20:41:45 +0200719
720 for (JsonNode cn : cpuNode) {
721 ObjectNode cpuObjNode = (ObjectNode) cn;
722
723 // CPU ID with its load and status
724 int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
725 float cpuLoad = cpuObjNode.path(CPU_PARAM_LOAD).floatValue();
726 boolean isBusy = cpuObjNode.path(CPU_PARAM_STATUS).booleanValue();
727
728 // Incorporate these statistics into an object
729 DefaultCpuStatistics.Builder cpuBuilder =
730 DefaultCpuStatistics.builder();
731
732 cpuBuilder.setDeviceId(deviceId)
733 .setId(cpuId)
734 .setLoad(cpuLoad)
735 .setIsBusy(isBusy)
736 .build();
737
738 // We have statistics for this CPU core
739 cpuStats.add(cpuBuilder.build());
740 }
741
742 return cpuStats;
743 }
744
745 /**
746 * Parse the input JSON object, looking for NIC-related
747 * statistics. Upon success, construct and return a list
748 * of NIC statistics objects.
749 *
750 * @param deviceId the device ID that sent the JSON object
751 * @param objNode input JSON node with NIC statistics information
752 * @return list of (per port) PortStatistics
753 */
Georgios Katsikas30bede52018-07-28 14:46:07 +0200754 private Collection<PortStatistics> parseNicStatistics(DeviceId deviceId, JsonNode objNode) {
755 if ((deviceId == null) || (objNode == null)) {
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200756 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200757 }
758
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200759 RestServerSBDevice device = null;
760 try {
761 device = (RestServerSBDevice) getController().getDevice(deviceId);
762 } catch (ClassCastException ccEx) {
763 return Collections.EMPTY_LIST;
764 }
Georgios Katsikas30bede52018-07-28 14:46:07 +0200765 if (device == null) {
766 return Collections.EMPTY_LIST;
767 }
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200768
769 Collection<PortStatistics> nicStats = Lists.newArrayList();
770
Georgios Katsikas83600982017-05-28 20:41:45 +0200771 JsonNode nicNode = objNode.path(PARAM_NICS);
772
773 for (JsonNode nn : nicNode) {
774 ObjectNode nicObjNode = (ObjectNode) nn;
775
776 // All the NIC attributes
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200777 String nicName = get(nn, NIC_PARAM_NAME);
778 checkArgument(!Strings.isNullOrEmpty(nicName), "NIC name is empty or NULL");
779
780 long portNumber = device.portNumberFromName(nicName);
781 checkArgument(portNumber >= 0, "Unknown port ID " + portNumber + " for NIC " + nicName);
Georgios Katsikas83600982017-05-28 20:41:45 +0200782
783 long rxCount = nicObjNode.path(NIC_STATS_RX_COUNT).asLong();
784 long rxBytes = nicObjNode.path(NIC_STATS_RX_BYTES).asLong();
785 long rxDropped = nicObjNode.path(NIC_STATS_RX_DROPS).asLong();
786 long rxErrors = nicObjNode.path(NIC_STATS_RX_ERRORS).asLong();
787 long txCount = nicObjNode.path(NIC_STATS_TX_COUNT).asLong();
788 long txBytes = nicObjNode.path(NIC_STATS_TX_BYTES).asLong();
789 long txDropped = nicObjNode.path(NIC_STATS_TX_DROPS).asLong();
790 long txErrors = nicObjNode.path(NIC_STATS_TX_ERRORS).asLong();
791
792 // Incorporate these statistics into an object
793 DefaultPortStatistics.Builder nicBuilder =
794 DefaultPortStatistics.builder();
795
796 nicBuilder.setDeviceId(deviceId)
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200797 .setPort((int) portNumber)
Georgios Katsikas83600982017-05-28 20:41:45 +0200798 .setPacketsReceived(rxCount)
799 .setPacketsSent(txCount)
800 .setBytesReceived(rxBytes)
801 .setBytesSent(txBytes)
802 .setPacketsRxDropped(rxDropped)
803 .setPacketsRxErrors(rxErrors)
804 .setPacketsTxDropped(txDropped)
805 .setPacketsTxErrors(txErrors)
806 .build();
807
808 // We have statistics for this NIC
809 nicStats.add(nicBuilder.build());
810 }
811
812 return nicStats;
813 }
814
815 /**
816 * Parse the input JSON object, looking for timing-related
817 * statistics. Upon success, construct and return a
818 * timing statistics object.
819 *
820 * @param objNode input JSON node with timing statistics information
821 * @return TimingStatistics object or null
822 */
823 private TimingStatistics parseTimingStatistics(JsonNode objNode) {
824 TimingStatistics timinsgStats = null;
825
826 if (objNode == null) {
827 return timinsgStats;
828 }
829
830 // Get timing statistics
831 JsonNode timingNode = objNode.path(PARAM_TIMING_STATS);
832 ObjectNode timingObjNode = (ObjectNode) timingNode;
833
834 // Time (ns) to parse the controller's deployment instruction
835 long parsingTime = timingObjNode.path(TIMING_PARAM_PARSE).asLong();
836 // Time (ns) to do the deployment
837 long launchingTime = timingObjNode.path(TIMING_PARAM_LAUNCH).asLong();
838 // Total time (ns)
839 long totalTime = parsingTime + launchingTime;
840
841 // Get autoscale timing statistics
842 JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_AUTOSCALE);
843 ObjectNode autoscaleTimingObjNode = (ObjectNode) autoscaleTimingNode;
844
845 // Time (ns) to autoscale a server's load
846 long autoscaleTime = autoscaleTimingObjNode.path(
847 TIMING_PARAM_AUTOSCALE
848 ).asLong();
849
850 DefaultTimingStatistics.Builder timingBuilder =
851 DefaultTimingStatistics.builder();
852
853 timingBuilder.setParsingTime(parsingTime)
854 .setLaunchingTime(launchingTime)
855 .setAutoscaleTime(autoscaleTime)
856 .build();
857
858 return timingBuilder.build();
859 }
860
861 /**
862 * Return a timing statistics object with zero counters.
863 * This is useful when constructing MonitoringStatistics
864 * objects that do not require timers.
865 *
866 * @return TimingStatistics object
867 */
868 private TimingStatistics getZeroTimingStatistics() {
869 DefaultTimingStatistics.Builder zeroTimingBuilder =
870 DefaultTimingStatistics.builder();
871
872 zeroTimingBuilder.setParsingTime(0)
873 .setLaunchingTime(0)
874 .setAutoscaleTime(0)
875 .build();
876
877 return zeroTimingBuilder.build();
878 }
879
880}