blob: e128e4e59edff7626dd29505154ec4386ffe2dbc [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
65import com.google.common.collect.Lists;
66import com.google.common.collect.ImmutableList;
67
68import javax.ws.rs.ProcessingException;
69import java.io.InputStream;
70import java.io.IOException;
71import java.net.URI;
72import java.net.URISyntaxException;
73import java.util.ArrayList;
74import java.util.Collection;
75import java.util.Collections;
76import java.util.HashSet;
77import java.util.HashMap;
78import java.util.List;
79import java.util.Map;
80import java.util.Set;
81import java.util.TreeSet;
82
83import static com.google.common.base.Preconditions.checkNotNull;
84import static org.slf4j.LoggerFactory.getLogger;
85
86/**
87 * Discovers the device details of
88 * REST-based commodity server devices.
89 */
90public class ServerDevicesDiscovery extends BasicServerDriver
91 implements DevicesDiscovery, DeviceDescriptionDiscovery,
92 PortStatisticsDiscovery, CpuStatisticsDiscovery,
93 MonitoringStatisticsDiscovery {
94
95 private final Logger log = getLogger(getClass());
96
97 /**
98 * Resource endpoints of the server agent (REST server-side).
99 */
100 private static final String RESOURCE_DISCOVERY_URL = BASE_URL + "/resources";
101 private static final String GLOBAL_STATS_URL = BASE_URL + "/stats";
102 private static final String SERVICE_CHAINS_STATS_URL = BASE_URL + "/chains_stats"; // + /ID
103
104 /**
105 * Parameters to be exchanged with the server's agent.
106 */
Georgios Katsikas83600982017-05-28 20:41:45 +0200107 private static final String PARAM_MANUFACTURER = "manufacturer";
108 private static final String PARAM_HW_VENDOR = "hwVersion";
109 private static final String PARAM_SW_VENDOR = "swVersion";
110 private static final String PARAM_SERIAL = "serial";
Georgios Katsikas83600982017-05-28 20:41:45 +0200111 private static final String PARAM_TIMING_STATS = "timing_stats";
112 private static final String PARAM_TIMING_AUTOSCALE = "autoscale_timing_stats";
113
114 private static final String NIC_PARAM_ID = "id";
115 private static final String NIC_PARAM_PORT_TYPE = "portType";
116 private static final String NIC_PARAM_PORT_TYPE_FIBER = "fiber";
117 private static final String NIC_PARAM_PORT_TYPE_COPPER = "copper";
118 private static final String NIC_PARAM_SPEED = "speed";
119 private static final String NIC_PARAM_STATUS = "status";
120 private static final String NIC_PARAM_HW_ADDR = "hwAddr";
Georgios Katsikas83600982017-05-28 20:41:45 +0200121
122 /**
123 * NIC statistics.
124 */
125 private static final String NIC_STATS_TX_COUNT = "txCount";
126 private static final String NIC_STATS_TX_BYTES = "txBytes";
127 private static final String NIC_STATS_TX_DROPS = "txDropped";
128 private static final String NIC_STATS_TX_ERRORS = "txErrors";
129 private static final String NIC_STATS_RX_COUNT = "rxCount";
130 private static final String NIC_STATS_RX_BYTES = "rxBytes";
131 private static final String NIC_STATS_RX_DROPS = "rxDropped";
132 private static final String NIC_STATS_RX_ERRORS = "rxErrors";
133
134 /**
135 * CPU statistics.
136 */
137 private static final String CPU_PARAM_ID = "id";
138 private static final String CPU_PARAM_VENDOR = "vendor";
139 private static final String CPU_PARAM_FREQUENCY = "frequency";
140 private static final String CPU_PARAM_LOAD = "load";
141 private static final String CPU_PARAM_STATUS = "busy";
142 private static final String CPU_STATS_BUSY_CPUS = "busyCpus";
143 private static final String CPU_STATS_FREE_CPUS = "freeCpus";
144
145 /**
146 * Timing statistics.
147 */
148 private static final String TIMING_PARAM_PARSE = "parse";
149 private static final String TIMING_PARAM_LAUNCH = "launch";
150 private static final String TIMING_PARAM_AUTOSCALE = "autoscale";
151
152 /**
153 * Auxiliary constants.
154 */
155 private static final short DISCOVERY_RETRIES = 3;
156 private static final String CPU_VENDOR_NULL = "Unsupported CPU vendor" +
157 " Choose one in: " + BasicServerDriver.enumTypesToString(CpuVendor.class);
158 private static final String NIC_RX_FILTER_NULL = "Unsupported NIC Rx filter" +
159 " Choose one in: " + BasicServerDriver.enumTypesToString(RxFilter.class);
160
161 /**
162 * Port types that usually appear in commodity servers.
163 */
164 public static final Map<String, Port.Type> PORT_TYPE_MAP =
165 Collections.unmodifiableMap(
166 new HashMap<String, Port.Type>() {
167 {
168 put(NIC_PARAM_PORT_TYPE_FIBER, Port.Type.FIBER);
169 put(NIC_PARAM_PORT_TYPE_COPPER, Port.Type.COPPER);
170 }
171 }
172 );
173
174 /**
175 * Constructs server device discovery.
176 */
177 public ServerDevicesDiscovery() {
178 super();
179 log.debug("Started");
180 }
181
182 @Override
183 public Set<DeviceId> deviceIds() {
184 // Set of devices to return
185 Set<DeviceId> devices = new HashSet<DeviceId>();
186
187 DeviceId deviceId = getHandler().data().deviceId();
188 checkNotNull(deviceId, DEVICE_ID_NULL);
189 devices.add(deviceId);
190
191 return devices;
192 }
193
194 @Override
195 public DeviceDescription deviceDetails(DeviceId deviceId) {
196 return getDeviceDetails(deviceId);
197 }
198
199 @Override
200 public DeviceDescription discoverDeviceDetails() {
201 return getDeviceDetails(null);
202 }
203
204 /**
205 * Query a server to retrieve its features.
206 *
207 * @param deviceId the device ID to be queried
208 * @return a DeviceDescription with the device's features
209 */
210 private DeviceDescription getDeviceDetails(DeviceId deviceId) {
211 // Create a description for this server device
212 ServerDeviceDescription desc = null;
213
214 // 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 {
227 response = getController().get(
228 deviceId,
229 RESOURCE_DISCOVERY_URL,
230 JSON
231 );
232 } catch (ProcessingException pEx) {
233 log.error("Failed to discover the device details of: {}", deviceId);
234 return desc;
235 }
236
237 // Load the JSON into objects
238 ObjectMapper mapper = new ObjectMapper();
239 Map<String, Object> jsonMap = null;
240 JsonNode jsonNode = null;
241 ObjectNode objNode = null;
242 try {
243 jsonMap = mapper.readValue(response, Map.class);
244 jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
245 objNode = (ObjectNode) jsonNode;
246 } catch (IOException ioEx) {
247 log.error("Failed to discover the device details of: {}", deviceId);
248 return desc;
249 }
250
251 if (jsonMap == null) {
252 log.error("Failed to discover the device details of: {}", deviceId);
253 return desc;
254 }
255
256 // Get all the attributes
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200257 String id = get(jsonNode, BasicServerDriver.PARAM_ID);
Georgios Katsikas83600982017-05-28 20:41:45 +0200258 String vendor = get(jsonNode, PARAM_MANUFACTURER);
259 String hw = get(jsonNode, PARAM_HW_VENDOR);
260 String sw = get(jsonNode, PARAM_SW_VENDOR);
261 String serial = get(jsonNode, PARAM_SERIAL);
262
263 // CPUs are composite attributes
264 Set<CpuDevice> cpuSet = new HashSet<CpuDevice>();
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200265 JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
Georgios Katsikas83600982017-05-28 20:41:45 +0200266
267 // Construct CPU objects
268 for (JsonNode cn : cpuNode) {
269 ObjectNode cpuObjNode = (ObjectNode) cn;
270
271 // All the CPU attributes
272 int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
273 String cpuVendorStr = get(cn, CPU_PARAM_VENDOR);
274 long cpuFrequency = cpuObjNode.path(CPU_PARAM_FREQUENCY).asLong();
275
276 // Verify that this is a valid vendor
277 CpuVendor cpuVendor = CpuVendor.getByName(cpuVendorStr);
278 checkNotNull(cpuVendor, CPU_VENDOR_NULL);
279
280 // Construct a CPU device
281 CpuDevice cpu = new DefaultCpuDevice(cpuId, cpuVendor, cpuFrequency);
282
283 // Add it to the set
284 cpuSet.add(cpu);
285 }
286
287 // NICs are composite attributes too
288 Set<NicDevice> nicSet = new HashSet<NicDevice>();
289 JsonNode nicNode = objNode.path(PARAM_NICS);
290
291 // Construct NIC objects
292 for (JsonNode nn : nicNode) {
293 ObjectNode nicObjNode = (ObjectNode) nn;
294
295 // All the NIC attributes
296 String nicId = get(nn, NIC_PARAM_ID);
297 int port = Integer.parseInt(nicId.replaceAll("\\D+", ""));
298 long speed = nicObjNode.path(NIC_PARAM_SPEED).asLong();
299 String portTypeStr = get(nn, NIC_PARAM_PORT_TYPE);
300 Port.Type portType = PORT_TYPE_MAP.get(portTypeStr);
301 if (portType == null) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800302 throw new IllegalArgumentException(
Georgios Katsikas83600982017-05-28 20:41:45 +0200303 portTypeStr + " is not a valid port type for NIC " + nicId
304 );
305 }
306 boolean status = nicObjNode.path(NIC_PARAM_STATUS).asInt() > 0;
307 String hwAddr = get(nn, NIC_PARAM_HW_ADDR);
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200308 JsonNode tagNode = nicObjNode.path(BasicServerDriver.NIC_PARAM_RX_FILTER);
Georgios Katsikas83600982017-05-28 20:41:45 +0200309 if (tagNode == null) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800310 throw new IllegalArgumentException(
Georgios Katsikas83600982017-05-28 20:41:45 +0200311 "The Rx filters of NIC " + nicId + " are not reported"
312 );
313 }
314
315 // Convert the JSON list into an array of strings
316 List<String> rxFilters = null;
317 try {
318 rxFilters = mapper.readValue(
319 tagNode.traverse(),
320 new TypeReference<ArrayList<String>>() { }
321 );
322 } catch (IOException ioEx) {
323 continue;
324 }
325
326 // Parse the array of strings and create an RxFilter object
327 NicRxFilter rxFilterMechanism = new NicRxFilter();
328 for (String s : rxFilters) {
329 // Verify that this is a valid Rx filter
330 RxFilter rf = RxFilter.getByName(s);
331 checkNotNull(rf, NIC_RX_FILTER_NULL);
332
333 rxFilterMechanism.addRxFilter(rf);
334 }
335
336 // Construct a NIC device for this server
337 NicDevice nic = new DefaultNicDevice(
338 nicId, port, portType, speed, status, hwAddr, rxFilterMechanism
339 );
340
341 // Add it to the set
342 nicSet.add(nic);
343 }
344
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200345 // Construct a complete server device object.
346 // Lists of NICs and CPUs extend the information
347 // already in RestSBDevice (parent class).
Georgios Katsikas83600982017-05-28 20:41:45 +0200348 RestServerSBDevice dev = new DefaultRestServerSBDevice(
349 device.ip(), device.port(), device.username(),
350 device.password(), device.protocol(), device.url(),
351 device.isActive(), device.testUrl().toString(),
352 vendor, hw, sw, AuthenticationScheme.BASIC, "",
353 cpuSet, nicSet
354 );
355 checkNotNull(dev, DEVICE_NULL);
356
357 // Updates the controller with the complete device information
358 getController().removeDevice(deviceId);
359 getController().addDevice((RestSBDevice) dev);
360
Georgios Katsikas83600982017-05-28 20:41:45 +0200361 try {
362 desc = new DefaultServerDeviceDescription(
Georgios Katsikas40ecef32018-07-03 11:17:44 +0200363 new URI(id), Device.Type.SERVER, vendor,
Georgios Katsikas83600982017-05-28 20:41:45 +0200364 hw, sw, serial, new ChassisId(),
365 cpuSet, nicSet, DefaultAnnotations.EMPTY
366 );
367 } catch (URISyntaxException uEx) {
368 log.error(
369 "Failed to create a server device description for: {}", deviceId
370 );
371 return null;
372 }
373
374 log.info("Device's {} details sent to the controller", deviceId);
375
376 return desc;
377 }
378
379 @Override
380 public List<PortDescription> discoverPortDetails() {
381 // List of port descriptions to return
382 List<PortDescription> portDescriptions = Lists.newArrayList();
383
384 // Retrieve the device ID
385 DeviceId deviceId = getHandler().data().deviceId();
386 checkNotNull(deviceId, DEVICE_ID_NULL);
387 // .. and object
388 RestServerSBDevice device = null;
389
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200390 // In case this method is called before discoverDeviceDetails,
391 // there is missing information to be gathered.
Georgios Katsikas83600982017-05-28 20:41:45 +0200392 short i = 0;
393 while ((device == null) && (i < DISCOVERY_RETRIES)) {
394 i++;
395
396 try {
397 device = (RestServerSBDevice) getController().getDevice(deviceId);
398 } catch (ClassCastException ccEx) {
399 try {
400 Thread.sleep(1);
401 } catch (InterruptedException intEx) {
402 // Just retry
Georgios Katsikas83600982017-05-28 20:41:45 +0200403 continue;
404 }
405 }
406
407 // No device
408 if (device == null) {
409 // This method will add the device to the RestSBController
410 this.getDeviceDetails(deviceId);
411 }
412 }
413
414 if ((device == null) || (device.nics() == null)) {
415 log.error("No ports available on {}", deviceId);
416 return ImmutableList.copyOf(portDescriptions);
417 }
418
419 // Sorted list of NIC ports
420 Set<NicDevice> nics = new TreeSet(device.nics());
421
422 // Iterate through the NICs of this device to populate the list
423 long portCounter = 0;
424 for (NicDevice nic : nics) {
425 // The port number of this NIC
426 PortNumber portNumber = PortNumber.portNumber(++portCounter);
427
428 // Include the name of this device as an annotation
429 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
430 .set(AnnotationKeys.PORT_NAME, nic.id());
431
432 // Create a port description and add it to the list
433 portDescriptions.add(
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800434 DefaultPortDescription.builder()
435 .withPortNumber(portNumber)
436 .isEnabled(nic.status())
437 .type(nic.portType())
438 .portSpeed(nic.speed())
439 .annotations(annotations.build())
440 .build());
Georgios Katsikas83600982017-05-28 20:41:45 +0200441
442 log.info(
443 "Port discovery on device {}: NIC {} is {} at {} Mbps",
444 deviceId, nic.port(), nic.status() ? "up" : "down",
445 nic.speed()
446 );
447 }
448
449 return ImmutableList.copyOf(portDescriptions);
450 }
451
452 @Override
453 public Collection<PortStatistics> discoverPortStatistics() {
454 // Retrieve the device ID
455 DeviceId deviceId = getHandler().data().deviceId();
456 checkNotNull(deviceId, DEVICE_ID_NULL);
457
458 // Get port statistics for this device
459 return getPortStatistics(deviceId);
460 }
461
462 /**
463 * Query a server to retrieve its port statistics.
464 *
465 * @param deviceId the device ID to be queried
466 * @return list of (per port) PortStatistics
467 */
468 private Collection<PortStatistics> getPortStatistics(DeviceId deviceId) {
469 // List of port statistics to return
470 Collection<PortStatistics> portStats = null;
471
472 // Get global monitoring statistics
473 MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
474 if (monStats == null) {
475 return portStats;
476 }
477
478 // Filter out the NIC statistics
479 portStats = monStats.nicStatisticsAll();
480 if (portStats == null) {
481 return portStats;
482 }
483
484 log.debug("Port statistics: {}", portStats.toString());
485
486 return portStats;
487 }
488
489 @Override
490 public Collection<CpuStatistics> discoverCpuStatistics() {
491 // Retrieve the device ID
492 DeviceId deviceId = getHandler().data().deviceId();
493 checkNotNull(deviceId, DEVICE_ID_NULL);
494
495 // Get CPU statistics for this device
496 return getCpuStatistics(deviceId);
497 }
498
499 /**
500 * Query a server to retrieve its CPU statistics.
501 *
502 * @param deviceId the device ID to be queried
503 * @return list of (per core) CpuStatistics
504 */
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200505 public Collection<CpuStatistics> getCpuStatistics(DeviceId deviceId) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200506 // List of port statistics to return
507 Collection<CpuStatistics> cpuStats = null;
508
509 // Get global monitoring statistics
510 MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
511 if (monStats == null) {
512 return cpuStats;
513 }
514
515 // Filter out the CPU statistics
516 cpuStats = monStats.cpuStatisticsAll();
517 if (cpuStats == null) {
518 return cpuStats;
519 }
520
521 log.debug("CPU statistics: {}", cpuStats.toString());
522
523 return cpuStats;
524 }
525
526 @Override
527 public MonitoringStatistics discoverGlobalMonitoringStatistics() {
528 // Retrieve the device ID
529 DeviceId deviceId = getHandler().data().deviceId();
530 checkNotNull(deviceId, DEVICE_ID_NULL);
531
532 // Get global monitoring statistics for this device
533 return getGlobalMonitoringStatistics(deviceId);
534 }
535
536 /**
537 * Query a server to retrieve its global monitoring statistics.
538 *
539 * @param deviceId the device ID to be queried
540 * @return global monitoring statistics
541 */
542 private MonitoringStatistics getGlobalMonitoringStatistics(DeviceId deviceId) {
543 // Monitoring statistics to return
544 MonitoringStatistics monStats = null;
545
546 RestServerSBDevice device = null;
547 try {
548 device = (RestServerSBDevice) getController().getDevice(deviceId);
549 } catch (ClassCastException ccEx) {
550 log.error(
551 "Failed to retrieve global monitoring statistics from device {}",
552 deviceId
553 );
554 return monStats;
555 }
556 checkNotNull(device, DEVICE_NULL);
557
558 // Hit the path that provides the server's global resources
559 InputStream response = null;
560 try {
561 response = getController().get(
562 deviceId,
563 GLOBAL_STATS_URL,
564 JSON
565 );
566 } catch (ProcessingException pEx) {
567 log.error(
568 "Failed to retrieve global monitoring statistics from device {}",
569 deviceId
570 );
571 return monStats;
572 }
573
574 // Load the JSON into objects
575 ObjectMapper mapper = new ObjectMapper();
576 Map<String, Object> jsonMap = null;
Georgios Katsikas83600982017-05-28 20:41:45 +0200577 ObjectNode objNode = null;
578 try {
579 jsonMap = mapper.readValue(response, Map.class);
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200580 JsonNode jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
Georgios Katsikas83600982017-05-28 20:41:45 +0200581 objNode = (ObjectNode) jsonNode;
582 } catch (IOException ioEx) {
583 log.error(
584 "Failed to retrieve global monitoring statistics from device {}",
585 deviceId
586 );
587 return monStats;
588 }
589
590 if (jsonMap == null) {
591 log.error(
592 "Failed to retrieve global monitoring statistics from device {}",
593 deviceId
594 );
595 return monStats;
596 }
597
598 // Get high-level CPU statistics
599 int busyCpus = objNode.path(CPU_STATS_BUSY_CPUS).asInt();
600 int freeCpus = objNode.path(CPU_STATS_FREE_CPUS).asInt();
601
602 // Get a list of CPU statistics per core
603 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
604
605 // Get a list of port statistics
606 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
607
608 // Get zero timing statistics
609 TimingStatistics timinsgStats = getZeroTimingStatistics();
610
611 // Ready to construct the grand object
612 DefaultMonitoringStatistics.Builder statsBuilder =
613 DefaultMonitoringStatistics.builder();
614
615 statsBuilder.setDeviceId(deviceId)
616 .setTimingStatistics(timinsgStats)
617 .setCpuStatistics(cpuStats)
618 .setNicStatistics(nicStats)
619 .build();
620
621 monStats = statsBuilder.build();
622
623 log.debug("Global monitoring statistics: {}", monStats.toString());
624
625 return monStats;
626 }
627
628 @Override
629 public MonitoringStatistics discoverMonitoringStatistics(URI tcId) {
630 // Retrieve the device ID
631 DeviceId deviceId = getHandler().data().deviceId();
632 checkNotNull(deviceId, DEVICE_ID_NULL);
633
634 // Get resource-specific monitoring statistics for this device
635 return getMonitoringStatistics(deviceId, tcId);
636 }
637
638 /**
639 * Query a server to retrieve monitoring statistics for a
640 * specific resource (i.e., traffic class).
641 *
642 * @param deviceId the device ID to be queried
643 * @param tcId the ID of the traffic class to be monitored
644 * @return resource-specific monitoring statistics
645 */
646 private MonitoringStatistics getMonitoringStatistics(DeviceId deviceId, URI tcId) {
647 // Monitoring statistics to return
648 MonitoringStatistics monStats = null;
649
650 RestServerSBDevice device = null;
651 try {
652 device = (RestServerSBDevice) getController().getDevice(deviceId);
653 } catch (ClassCastException ccEx) {
654 log.error(
655 "Failed to retrieve monitoring statistics from device {}",
656 deviceId
657 );
658 return monStats;
659 }
660 checkNotNull(device, DEVICE_NULL);
661
662 // Create a resource-specific URL
663 String scUrl = SERVICE_CHAINS_STATS_URL + "/" + tcId.toString();
664
665 // Hit the path that provides the server's specific resources
666 InputStream response = null;
667 try {
668 response = getController().get(
669 deviceId,
670 scUrl,
671 JSON
672 );
673 } catch (ProcessingException pEx) {
674 log.error(
675 "Failed to retrieve monitoring statistics from device {}",
676 deviceId
677 );
678 return monStats;
679 }
680
681 // Load the JSON into objects
682 ObjectMapper mapper = new ObjectMapper();
683 Map<String, Object> jsonMap = null;
684 JsonNode jsonNode = null;
685 ObjectNode objNode = null;
686 try {
687 jsonMap = mapper.readValue(response, Map.class);
688 jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
689 objNode = (ObjectNode) jsonNode;
690 } catch (IOException ioEx) {
691 log.error(
692 "Failed to retrieve monitoring statistics from device {}",
693 deviceId
694 );
695 return monStats;
696 }
697
698 if (jsonMap == null) {
699 log.error(
700 "Failed to retrieve monitoring statistics from device {}",
701 deviceId
702 );
703 return monStats;
704 }
705
706 // Get the ID of the traffic class
707 String id = get(jsonNode, PARAM_ID);
708
709 // And verify that this is the traffic class we want to monitor
710 if (!id.equals(tcId.toString())) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800711 throw new IllegalStateException(
Georgios Katsikas83600982017-05-28 20:41:45 +0200712 "Failed to retrieve monitoring data for traffic class " + tcId +
713 ". Traffic class ID does not agree."
714 );
715 }
716
717 // Get a list of CPU statistics per core
718 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
719
720 // Get a list of port statistics
721 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
722
723 // Get timing statistics
724 TimingStatistics timinsgStats = parseTimingStatistics(objNode);
725
726 // Ready to construct the grand object
727 DefaultMonitoringStatistics.Builder statsBuilder =
728 DefaultMonitoringStatistics.builder();
729
730 statsBuilder.setDeviceId(deviceId)
731 .setTimingStatistics(timinsgStats)
732 .setCpuStatistics(cpuStats)
733 .setNicStatistics(nicStats)
734 .build();
735
736 monStats = statsBuilder.build();
737
738 log.debug("Monitoring statistics: {}", monStats.toString());
739
740 return monStats;
741 }
742
743 /**
744 * Parse the input JSON object, looking for CPU-related
745 * statistics. Upon success, construct and return a list
746 * of CPU statistics objects.
747 *
748 * @param deviceId the device ID that sent the JSON object
749 * @param objNode input JSON node with CPU statistics information
750 * @return list of (per core) CpuStatistics
751 */
752 private Collection<CpuStatistics> parseCpuStatistics(
753 DeviceId deviceId, JsonNode objNode) {
754 Collection<CpuStatistics> cpuStats = Lists.newArrayList();
755
756 if (objNode == null) {
757 return cpuStats;
758 }
759
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200760 JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
Georgios Katsikas83600982017-05-28 20:41:45 +0200761
762 for (JsonNode cn : cpuNode) {
763 ObjectNode cpuObjNode = (ObjectNode) cn;
764
765 // CPU ID with its load and status
766 int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
767 float cpuLoad = cpuObjNode.path(CPU_PARAM_LOAD).floatValue();
768 boolean isBusy = cpuObjNode.path(CPU_PARAM_STATUS).booleanValue();
769
770 // Incorporate these statistics into an object
771 DefaultCpuStatistics.Builder cpuBuilder =
772 DefaultCpuStatistics.builder();
773
774 cpuBuilder.setDeviceId(deviceId)
775 .setId(cpuId)
776 .setLoad(cpuLoad)
777 .setIsBusy(isBusy)
778 .build();
779
780 // We have statistics for this CPU core
781 cpuStats.add(cpuBuilder.build());
782 }
783
784 return cpuStats;
785 }
786
787 /**
788 * Parse the input JSON object, looking for NIC-related
789 * statistics. Upon success, construct and return a list
790 * of NIC statistics objects.
791 *
792 * @param deviceId the device ID that sent the JSON object
793 * @param objNode input JSON node with NIC statistics information
794 * @return list of (per port) PortStatistics
795 */
796 private Collection<PortStatistics> parseNicStatistics(
797 DeviceId deviceId, JsonNode objNode) {
798 Collection<PortStatistics> nicStats = Lists.newArrayList();
799
800 if (objNode == null) {
801 return nicStats;
802 }
803
804 JsonNode nicNode = objNode.path(PARAM_NICS);
805
806 for (JsonNode nn : nicNode) {
807 ObjectNode nicObjNode = (ObjectNode) nn;
808
809 // All the NIC attributes
810 String nicId = get(nn, NIC_PARAM_ID);
811 int port = Integer.parseInt(nicId.replaceAll("\\D+", ""));
812
813 long rxCount = nicObjNode.path(NIC_STATS_RX_COUNT).asLong();
814 long rxBytes = nicObjNode.path(NIC_STATS_RX_BYTES).asLong();
815 long rxDropped = nicObjNode.path(NIC_STATS_RX_DROPS).asLong();
816 long rxErrors = nicObjNode.path(NIC_STATS_RX_ERRORS).asLong();
817 long txCount = nicObjNode.path(NIC_STATS_TX_COUNT).asLong();
818 long txBytes = nicObjNode.path(NIC_STATS_TX_BYTES).asLong();
819 long txDropped = nicObjNode.path(NIC_STATS_TX_DROPS).asLong();
820 long txErrors = nicObjNode.path(NIC_STATS_TX_ERRORS).asLong();
821
822 // Incorporate these statistics into an object
823 DefaultPortStatistics.Builder nicBuilder =
824 DefaultPortStatistics.builder();
825
826 nicBuilder.setDeviceId(deviceId)
827 .setPort(port)
828 .setPacketsReceived(rxCount)
829 .setPacketsSent(txCount)
830 .setBytesReceived(rxBytes)
831 .setBytesSent(txBytes)
832 .setPacketsRxDropped(rxDropped)
833 .setPacketsRxErrors(rxErrors)
834 .setPacketsTxDropped(txDropped)
835 .setPacketsTxErrors(txErrors)
836 .build();
837
838 // We have statistics for this NIC
839 nicStats.add(nicBuilder.build());
840 }
841
842 return nicStats;
843 }
844
845 /**
846 * Parse the input JSON object, looking for timing-related
847 * statistics. Upon success, construct and return a
848 * timing statistics object.
849 *
850 * @param objNode input JSON node with timing statistics information
851 * @return TimingStatistics object or null
852 */
853 private TimingStatistics parseTimingStatistics(JsonNode objNode) {
854 TimingStatistics timinsgStats = null;
855
856 if (objNode == null) {
857 return timinsgStats;
858 }
859
860 // Get timing statistics
861 JsonNode timingNode = objNode.path(PARAM_TIMING_STATS);
862 ObjectNode timingObjNode = (ObjectNode) timingNode;
863
864 // Time (ns) to parse the controller's deployment instruction
865 long parsingTime = timingObjNode.path(TIMING_PARAM_PARSE).asLong();
866 // Time (ns) to do the deployment
867 long launchingTime = timingObjNode.path(TIMING_PARAM_LAUNCH).asLong();
868 // Total time (ns)
869 long totalTime = parsingTime + launchingTime;
870
871 // Get autoscale timing statistics
872 JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_AUTOSCALE);
873 ObjectNode autoscaleTimingObjNode = (ObjectNode) autoscaleTimingNode;
874
875 // Time (ns) to autoscale a server's load
876 long autoscaleTime = autoscaleTimingObjNode.path(
877 TIMING_PARAM_AUTOSCALE
878 ).asLong();
879
880 DefaultTimingStatistics.Builder timingBuilder =
881 DefaultTimingStatistics.builder();
882
883 timingBuilder.setParsingTime(parsingTime)
884 .setLaunchingTime(launchingTime)
885 .setAutoscaleTime(autoscaleTime)
886 .build();
887
888 return timingBuilder.build();
889 }
890
891 /**
892 * Return a timing statistics object with zero counters.
893 * This is useful when constructing MonitoringStatistics
894 * objects that do not require timers.
895 *
896 * @return TimingStatistics object
897 */
898 private TimingStatistics getZeroTimingStatistics() {
899 DefaultTimingStatistics.Builder zeroTimingBuilder =
900 DefaultTimingStatistics.builder();
901
902 zeroTimingBuilder.setParsingTime(0)
903 .setLaunchingTime(0)
904 .setAutoscaleTime(0)
905 .build();
906
907 return zeroTimingBuilder.build();
908 }
909
910}