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