blob: 8a009f3bf3820d55346e08f564c3648b71129468 [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
361 /**
362 * TODO: Create a new Device type
363 * Device.Type.COMMODITY_SERVER
364 * and add a new icon in the GUI.
365 */
366 try {
367 desc = new DefaultServerDeviceDescription(
368 new URI(id), Device.Type.OTHER, vendor,
369 hw, sw, serial, new ChassisId(),
370 cpuSet, nicSet, DefaultAnnotations.EMPTY
371 );
372 } catch (URISyntaxException uEx) {
373 log.error(
374 "Failed to create a server device description for: {}", deviceId
375 );
376 return null;
377 }
378
379 log.info("Device's {} details sent to the controller", deviceId);
380
381 return desc;
382 }
383
384 @Override
385 public List<PortDescription> discoverPortDetails() {
386 // List of port descriptions to return
387 List<PortDescription> portDescriptions = Lists.newArrayList();
388
389 // Retrieve the device ID
390 DeviceId deviceId = getHandler().data().deviceId();
391 checkNotNull(deviceId, DEVICE_ID_NULL);
392 // .. and object
393 RestServerSBDevice device = null;
394
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200395 // In case this method is called before discoverDeviceDetails,
396 // there is missing information to be gathered.
Georgios Katsikas83600982017-05-28 20:41:45 +0200397 short i = 0;
398 while ((device == null) && (i < DISCOVERY_RETRIES)) {
399 i++;
400
401 try {
402 device = (RestServerSBDevice) getController().getDevice(deviceId);
403 } catch (ClassCastException ccEx) {
404 try {
405 Thread.sleep(1);
406 } catch (InterruptedException intEx) {
407 // Just retry
Georgios Katsikas83600982017-05-28 20:41:45 +0200408 continue;
409 }
410 }
411
412 // No device
413 if (device == null) {
414 // This method will add the device to the RestSBController
415 this.getDeviceDetails(deviceId);
416 }
417 }
418
419 if ((device == null) || (device.nics() == null)) {
420 log.error("No ports available on {}", deviceId);
421 return ImmutableList.copyOf(portDescriptions);
422 }
423
424 // Sorted list of NIC ports
425 Set<NicDevice> nics = new TreeSet(device.nics());
426
427 // Iterate through the NICs of this device to populate the list
428 long portCounter = 0;
429 for (NicDevice nic : nics) {
430 // The port number of this NIC
431 PortNumber portNumber = PortNumber.portNumber(++portCounter);
432
433 // Include the name of this device as an annotation
434 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
435 .set(AnnotationKeys.PORT_NAME, nic.id());
436
437 // Create a port description and add it to the list
438 portDescriptions.add(
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800439 DefaultPortDescription.builder()
440 .withPortNumber(portNumber)
441 .isEnabled(nic.status())
442 .type(nic.portType())
443 .portSpeed(nic.speed())
444 .annotations(annotations.build())
445 .build());
Georgios Katsikas83600982017-05-28 20:41:45 +0200446
447 log.info(
448 "Port discovery on device {}: NIC {} is {} at {} Mbps",
449 deviceId, nic.port(), nic.status() ? "up" : "down",
450 nic.speed()
451 );
452 }
453
454 return ImmutableList.copyOf(portDescriptions);
455 }
456
457 @Override
458 public Collection<PortStatistics> discoverPortStatistics() {
459 // Retrieve the device ID
460 DeviceId deviceId = getHandler().data().deviceId();
461 checkNotNull(deviceId, DEVICE_ID_NULL);
462
463 // Get port statistics for this device
464 return getPortStatistics(deviceId);
465 }
466
467 /**
468 * Query a server to retrieve its port statistics.
469 *
470 * @param deviceId the device ID to be queried
471 * @return list of (per port) PortStatistics
472 */
473 private Collection<PortStatistics> getPortStatistics(DeviceId deviceId) {
474 // List of port statistics to return
475 Collection<PortStatistics> portStats = null;
476
477 // Get global monitoring statistics
478 MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
479 if (monStats == null) {
480 return portStats;
481 }
482
483 // Filter out the NIC statistics
484 portStats = monStats.nicStatisticsAll();
485 if (portStats == null) {
486 return portStats;
487 }
488
489 log.debug("Port statistics: {}", portStats.toString());
490
491 return portStats;
492 }
493
494 @Override
495 public Collection<CpuStatistics> discoverCpuStatistics() {
496 // Retrieve the device ID
497 DeviceId deviceId = getHandler().data().deviceId();
498 checkNotNull(deviceId, DEVICE_ID_NULL);
499
500 // Get CPU statistics for this device
501 return getCpuStatistics(deviceId);
502 }
503
504 /**
505 * Query a server to retrieve its CPU statistics.
506 *
507 * @param deviceId the device ID to be queried
508 * @return list of (per core) CpuStatistics
509 */
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200510 public Collection<CpuStatistics> getCpuStatistics(DeviceId deviceId) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200511 // List of port statistics to return
512 Collection<CpuStatistics> cpuStats = null;
513
514 // Get global monitoring statistics
515 MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
516 if (monStats == null) {
517 return cpuStats;
518 }
519
520 // Filter out the CPU statistics
521 cpuStats = monStats.cpuStatisticsAll();
522 if (cpuStats == null) {
523 return cpuStats;
524 }
525
526 log.debug("CPU statistics: {}", cpuStats.toString());
527
528 return cpuStats;
529 }
530
531 @Override
532 public MonitoringStatistics discoverGlobalMonitoringStatistics() {
533 // Retrieve the device ID
534 DeviceId deviceId = getHandler().data().deviceId();
535 checkNotNull(deviceId, DEVICE_ID_NULL);
536
537 // Get global monitoring statistics for this device
538 return getGlobalMonitoringStatistics(deviceId);
539 }
540
541 /**
542 * Query a server to retrieve its global monitoring statistics.
543 *
544 * @param deviceId the device ID to be queried
545 * @return global monitoring statistics
546 */
547 private MonitoringStatistics getGlobalMonitoringStatistics(DeviceId deviceId) {
548 // Monitoring statistics to return
549 MonitoringStatistics monStats = null;
550
551 RestServerSBDevice device = null;
552 try {
553 device = (RestServerSBDevice) getController().getDevice(deviceId);
554 } catch (ClassCastException ccEx) {
555 log.error(
556 "Failed to retrieve global monitoring statistics from device {}",
557 deviceId
558 );
559 return monStats;
560 }
561 checkNotNull(device, DEVICE_NULL);
562
563 // Hit the path that provides the server's global resources
564 InputStream response = null;
565 try {
566 response = getController().get(
567 deviceId,
568 GLOBAL_STATS_URL,
569 JSON
570 );
571 } catch (ProcessingException pEx) {
572 log.error(
573 "Failed to retrieve global monitoring statistics from device {}",
574 deviceId
575 );
576 return monStats;
577 }
578
579 // Load the JSON into objects
580 ObjectMapper mapper = new ObjectMapper();
581 Map<String, Object> jsonMap = null;
Georgios Katsikas83600982017-05-28 20:41:45 +0200582 ObjectNode objNode = null;
583 try {
584 jsonMap = mapper.readValue(response, Map.class);
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200585 JsonNode jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
Georgios Katsikas83600982017-05-28 20:41:45 +0200586 objNode = (ObjectNode) jsonNode;
587 } catch (IOException ioEx) {
588 log.error(
589 "Failed to retrieve global monitoring statistics from device {}",
590 deviceId
591 );
592 return monStats;
593 }
594
595 if (jsonMap == null) {
596 log.error(
597 "Failed to retrieve global monitoring statistics from device {}",
598 deviceId
599 );
600 return monStats;
601 }
602
603 // Get high-level CPU statistics
604 int busyCpus = objNode.path(CPU_STATS_BUSY_CPUS).asInt();
605 int freeCpus = objNode.path(CPU_STATS_FREE_CPUS).asInt();
606
607 // Get a list of CPU statistics per core
608 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
609
610 // Get a list of port statistics
611 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
612
613 // Get zero timing statistics
614 TimingStatistics timinsgStats = getZeroTimingStatistics();
615
616 // Ready to construct the grand object
617 DefaultMonitoringStatistics.Builder statsBuilder =
618 DefaultMonitoringStatistics.builder();
619
620 statsBuilder.setDeviceId(deviceId)
621 .setTimingStatistics(timinsgStats)
622 .setCpuStatistics(cpuStats)
623 .setNicStatistics(nicStats)
624 .build();
625
626 monStats = statsBuilder.build();
627
628 log.debug("Global monitoring statistics: {}", monStats.toString());
629
630 return monStats;
631 }
632
633 @Override
634 public MonitoringStatistics discoverMonitoringStatistics(URI tcId) {
635 // Retrieve the device ID
636 DeviceId deviceId = getHandler().data().deviceId();
637 checkNotNull(deviceId, DEVICE_ID_NULL);
638
639 // Get resource-specific monitoring statistics for this device
640 return getMonitoringStatistics(deviceId, tcId);
641 }
642
643 /**
644 * Query a server to retrieve monitoring statistics for a
645 * specific resource (i.e., traffic class).
646 *
647 * @param deviceId the device ID to be queried
648 * @param tcId the ID of the traffic class to be monitored
649 * @return resource-specific monitoring statistics
650 */
651 private MonitoringStatistics getMonitoringStatistics(DeviceId deviceId, URI tcId) {
652 // Monitoring statistics to return
653 MonitoringStatistics monStats = null;
654
655 RestServerSBDevice device = null;
656 try {
657 device = (RestServerSBDevice) getController().getDevice(deviceId);
658 } catch (ClassCastException ccEx) {
659 log.error(
660 "Failed to retrieve monitoring statistics from device {}",
661 deviceId
662 );
663 return monStats;
664 }
665 checkNotNull(device, DEVICE_NULL);
666
667 // Create a resource-specific URL
668 String scUrl = SERVICE_CHAINS_STATS_URL + "/" + tcId.toString();
669
670 // Hit the path that provides the server's specific resources
671 InputStream response = null;
672 try {
673 response = getController().get(
674 deviceId,
675 scUrl,
676 JSON
677 );
678 } catch (ProcessingException pEx) {
679 log.error(
680 "Failed to retrieve monitoring statistics from device {}",
681 deviceId
682 );
683 return monStats;
684 }
685
686 // Load the JSON into objects
687 ObjectMapper mapper = new ObjectMapper();
688 Map<String, Object> jsonMap = null;
689 JsonNode jsonNode = null;
690 ObjectNode objNode = null;
691 try {
692 jsonMap = mapper.readValue(response, Map.class);
693 jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
694 objNode = (ObjectNode) jsonNode;
695 } catch (IOException ioEx) {
696 log.error(
697 "Failed to retrieve monitoring statistics from device {}",
698 deviceId
699 );
700 return monStats;
701 }
702
703 if (jsonMap == null) {
704 log.error(
705 "Failed to retrieve monitoring statistics from device {}",
706 deviceId
707 );
708 return monStats;
709 }
710
711 // Get the ID of the traffic class
712 String id = get(jsonNode, PARAM_ID);
713
714 // And verify that this is the traffic class we want to monitor
715 if (!id.equals(tcId.toString())) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800716 throw new IllegalStateException(
Georgios Katsikas83600982017-05-28 20:41:45 +0200717 "Failed to retrieve monitoring data for traffic class " + tcId +
718 ". Traffic class ID does not agree."
719 );
720 }
721
722 // Get a list of CPU statistics per core
723 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
724
725 // Get a list of port statistics
726 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
727
728 // Get timing statistics
729 TimingStatistics timinsgStats = parseTimingStatistics(objNode);
730
731 // Ready to construct the grand object
732 DefaultMonitoringStatistics.Builder statsBuilder =
733 DefaultMonitoringStatistics.builder();
734
735 statsBuilder.setDeviceId(deviceId)
736 .setTimingStatistics(timinsgStats)
737 .setCpuStatistics(cpuStats)
738 .setNicStatistics(nicStats)
739 .build();
740
741 monStats = statsBuilder.build();
742
743 log.debug("Monitoring statistics: {}", monStats.toString());
744
745 return monStats;
746 }
747
748 /**
749 * Parse the input JSON object, looking for CPU-related
750 * statistics. Upon success, construct and return a list
751 * of CPU statistics objects.
752 *
753 * @param deviceId the device ID that sent the JSON object
754 * @param objNode input JSON node with CPU statistics information
755 * @return list of (per core) CpuStatistics
756 */
757 private Collection<CpuStatistics> parseCpuStatistics(
758 DeviceId deviceId, JsonNode objNode) {
759 Collection<CpuStatistics> cpuStats = Lists.newArrayList();
760
761 if (objNode == null) {
762 return cpuStats;
763 }
764
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200765 JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
Georgios Katsikas83600982017-05-28 20:41:45 +0200766
767 for (JsonNode cn : cpuNode) {
768 ObjectNode cpuObjNode = (ObjectNode) cn;
769
770 // CPU ID with its load and status
771 int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
772 float cpuLoad = cpuObjNode.path(CPU_PARAM_LOAD).floatValue();
773 boolean isBusy = cpuObjNode.path(CPU_PARAM_STATUS).booleanValue();
774
775 // Incorporate these statistics into an object
776 DefaultCpuStatistics.Builder cpuBuilder =
777 DefaultCpuStatistics.builder();
778
779 cpuBuilder.setDeviceId(deviceId)
780 .setId(cpuId)
781 .setLoad(cpuLoad)
782 .setIsBusy(isBusy)
783 .build();
784
785 // We have statistics for this CPU core
786 cpuStats.add(cpuBuilder.build());
787 }
788
789 return cpuStats;
790 }
791
792 /**
793 * Parse the input JSON object, looking for NIC-related
794 * statistics. Upon success, construct and return a list
795 * of NIC statistics objects.
796 *
797 * @param deviceId the device ID that sent the JSON object
798 * @param objNode input JSON node with NIC statistics information
799 * @return list of (per port) PortStatistics
800 */
801 private Collection<PortStatistics> parseNicStatistics(
802 DeviceId deviceId, JsonNode objNode) {
803 Collection<PortStatistics> nicStats = Lists.newArrayList();
804
805 if (objNode == null) {
806 return nicStats;
807 }
808
809 JsonNode nicNode = objNode.path(PARAM_NICS);
810
811 for (JsonNode nn : nicNode) {
812 ObjectNode nicObjNode = (ObjectNode) nn;
813
814 // All the NIC attributes
815 String nicId = get(nn, NIC_PARAM_ID);
816 int port = Integer.parseInt(nicId.replaceAll("\\D+", ""));
817
818 long rxCount = nicObjNode.path(NIC_STATS_RX_COUNT).asLong();
819 long rxBytes = nicObjNode.path(NIC_STATS_RX_BYTES).asLong();
820 long rxDropped = nicObjNode.path(NIC_STATS_RX_DROPS).asLong();
821 long rxErrors = nicObjNode.path(NIC_STATS_RX_ERRORS).asLong();
822 long txCount = nicObjNode.path(NIC_STATS_TX_COUNT).asLong();
823 long txBytes = nicObjNode.path(NIC_STATS_TX_BYTES).asLong();
824 long txDropped = nicObjNode.path(NIC_STATS_TX_DROPS).asLong();
825 long txErrors = nicObjNode.path(NIC_STATS_TX_ERRORS).asLong();
826
827 // Incorporate these statistics into an object
828 DefaultPortStatistics.Builder nicBuilder =
829 DefaultPortStatistics.builder();
830
831 nicBuilder.setDeviceId(deviceId)
832 .setPort(port)
833 .setPacketsReceived(rxCount)
834 .setPacketsSent(txCount)
835 .setBytesReceived(rxBytes)
836 .setBytesSent(txBytes)
837 .setPacketsRxDropped(rxDropped)
838 .setPacketsRxErrors(rxErrors)
839 .setPacketsTxDropped(txDropped)
840 .setPacketsTxErrors(txErrors)
841 .build();
842
843 // We have statistics for this NIC
844 nicStats.add(nicBuilder.build());
845 }
846
847 return nicStats;
848 }
849
850 /**
851 * Parse the input JSON object, looking for timing-related
852 * statistics. Upon success, construct and return a
853 * timing statistics object.
854 *
855 * @param objNode input JSON node with timing statistics information
856 * @return TimingStatistics object or null
857 */
858 private TimingStatistics parseTimingStatistics(JsonNode objNode) {
859 TimingStatistics timinsgStats = null;
860
861 if (objNode == null) {
862 return timinsgStats;
863 }
864
865 // Get timing statistics
866 JsonNode timingNode = objNode.path(PARAM_TIMING_STATS);
867 ObjectNode timingObjNode = (ObjectNode) timingNode;
868
869 // Time (ns) to parse the controller's deployment instruction
870 long parsingTime = timingObjNode.path(TIMING_PARAM_PARSE).asLong();
871 // Time (ns) to do the deployment
872 long launchingTime = timingObjNode.path(TIMING_PARAM_LAUNCH).asLong();
873 // Total time (ns)
874 long totalTime = parsingTime + launchingTime;
875
876 // Get autoscale timing statistics
877 JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_AUTOSCALE);
878 ObjectNode autoscaleTimingObjNode = (ObjectNode) autoscaleTimingNode;
879
880 // Time (ns) to autoscale a server's load
881 long autoscaleTime = autoscaleTimingObjNode.path(
882 TIMING_PARAM_AUTOSCALE
883 ).asLong();
884
885 DefaultTimingStatistics.Builder timingBuilder =
886 DefaultTimingStatistics.builder();
887
888 timingBuilder.setParsingTime(parsingTime)
889 .setLaunchingTime(launchingTime)
890 .setAutoscaleTime(autoscaleTime)
891 .build();
892
893 return timingBuilder.build();
894 }
895
896 /**
897 * Return a timing statistics object with zero counters.
898 * This is useful when constructing MonitoringStatistics
899 * objects that do not require timers.
900 *
901 * @return TimingStatistics object
902 */
903 private TimingStatistics getZeroTimingStatistics() {
904 DefaultTimingStatistics.Builder zeroTimingBuilder =
905 DefaultTimingStatistics.builder();
906
907 zeroTimingBuilder.setParsingTime(0)
908 .setLaunchingTime(0)
909 .setAutoscaleTime(0)
910 .build();
911
912 return zeroTimingBuilder.build();
913 }
914
915}