blob: 808c1d43a34f04bc84d4f08a16946ea17b424924 [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 {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200227 response = getController().get(deviceId, RESOURCE_DISCOVERY_URL, JSON);
Georgios Katsikas83600982017-05-28 20:41:45 +0200228 } catch (ProcessingException pEx) {
229 log.error("Failed to discover the device details of: {}", deviceId);
230 return desc;
231 }
232
233 // Load the JSON into objects
234 ObjectMapper mapper = new ObjectMapper();
235 Map<String, Object> jsonMap = null;
236 JsonNode jsonNode = null;
237 ObjectNode objNode = null;
238 try {
239 jsonMap = mapper.readValue(response, Map.class);
240 jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
241 objNode = (ObjectNode) jsonNode;
242 } catch (IOException ioEx) {
243 log.error("Failed to discover the device details of: {}", deviceId);
244 return desc;
245 }
246
247 if (jsonMap == null) {
248 log.error("Failed to discover the device details of: {}", deviceId);
249 return desc;
250 }
251
252 // Get all the attributes
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200253 String id = get(jsonNode, BasicServerDriver.PARAM_ID);
Georgios Katsikas83600982017-05-28 20:41:45 +0200254 String vendor = get(jsonNode, PARAM_MANUFACTURER);
255 String hw = get(jsonNode, PARAM_HW_VENDOR);
256 String sw = get(jsonNode, PARAM_SW_VENDOR);
257 String serial = get(jsonNode, PARAM_SERIAL);
258
259 // CPUs are composite attributes
260 Set<CpuDevice> cpuSet = new HashSet<CpuDevice>();
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200261 JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
Georgios Katsikas83600982017-05-28 20:41:45 +0200262
263 // Construct CPU objects
264 for (JsonNode cn : cpuNode) {
265 ObjectNode cpuObjNode = (ObjectNode) cn;
266
267 // All the CPU attributes
268 int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
269 String cpuVendorStr = get(cn, CPU_PARAM_VENDOR);
270 long cpuFrequency = cpuObjNode.path(CPU_PARAM_FREQUENCY).asLong();
271
272 // Verify that this is a valid vendor
273 CpuVendor cpuVendor = CpuVendor.getByName(cpuVendorStr);
274 checkNotNull(cpuVendor, CPU_VENDOR_NULL);
275
276 // Construct a CPU device
277 CpuDevice cpu = new DefaultCpuDevice(cpuId, cpuVendor, cpuFrequency);
278
279 // Add it to the set
280 cpuSet.add(cpu);
281 }
282
283 // NICs are composite attributes too
284 Set<NicDevice> nicSet = new HashSet<NicDevice>();
285 JsonNode nicNode = objNode.path(PARAM_NICS);
286
287 // Construct NIC objects
288 for (JsonNode nn : nicNode) {
289 ObjectNode nicObjNode = (ObjectNode) nn;
290
291 // All the NIC attributes
292 String nicId = get(nn, NIC_PARAM_ID);
293 int port = Integer.parseInt(nicId.replaceAll("\\D+", ""));
294 long speed = nicObjNode.path(NIC_PARAM_SPEED).asLong();
295 String portTypeStr = get(nn, NIC_PARAM_PORT_TYPE);
296 Port.Type portType = PORT_TYPE_MAP.get(portTypeStr);
297 if (portType == null) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800298 throw new IllegalArgumentException(
Georgios Katsikasd472a322018-07-08 19:58:25 +0200299 portTypeStr + " is not a valid port type for NIC " + nicId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200300 }
301 boolean status = nicObjNode.path(NIC_PARAM_STATUS).asInt() > 0;
302 String hwAddr = get(nn, NIC_PARAM_HW_ADDR);
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200303 JsonNode tagNode = nicObjNode.path(BasicServerDriver.NIC_PARAM_RX_FILTER);
Georgios Katsikas83600982017-05-28 20:41:45 +0200304 if (tagNode == null) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800305 throw new IllegalArgumentException(
Georgios Katsikasd472a322018-07-08 19:58:25 +0200306 "The Rx filters of NIC " + nicId + " are not reported");
Georgios Katsikas83600982017-05-28 20:41:45 +0200307 }
308
309 // Convert the JSON list into an array of strings
310 List<String> rxFilters = null;
311 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200312 rxFilters = mapper.readValue(tagNode.traverse(),
313 new TypeReference<ArrayList<String>>() { });
Georgios Katsikas83600982017-05-28 20:41:45 +0200314 } catch (IOException ioEx) {
315 continue;
316 }
317
318 // Parse the array of strings and create an RxFilter object
319 NicRxFilter rxFilterMechanism = new NicRxFilter();
320 for (String s : rxFilters) {
321 // Verify that this is a valid Rx filter
322 RxFilter rf = RxFilter.getByName(s);
323 checkNotNull(rf, NIC_RX_FILTER_NULL);
324
325 rxFilterMechanism.addRxFilter(rf);
326 }
327
328 // Construct a NIC device for this server
329 NicDevice nic = new DefaultNicDevice(
330 nicId, port, portType, speed, status, hwAddr, rxFilterMechanism
331 );
332
333 // Add it to the set
334 nicSet.add(nic);
335 }
336
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200337 // Construct a complete server device object.
338 // Lists of NICs and CPUs extend the information
339 // already in RestSBDevice (parent class).
Georgios Katsikas83600982017-05-28 20:41:45 +0200340 RestServerSBDevice dev = new DefaultRestServerSBDevice(
341 device.ip(), device.port(), device.username(),
342 device.password(), device.protocol(), device.url(),
343 device.isActive(), device.testUrl().toString(),
344 vendor, hw, sw, AuthenticationScheme.BASIC, "",
345 cpuSet, nicSet
346 );
347 checkNotNull(dev, DEVICE_NULL);
348
349 // Updates the controller with the complete device information
350 getController().removeDevice(deviceId);
351 getController().addDevice((RestSBDevice) dev);
352
Georgios Katsikas83600982017-05-28 20:41:45 +0200353 try {
354 desc = new DefaultServerDeviceDescription(
Georgios Katsikas40ecef32018-07-03 11:17:44 +0200355 new URI(id), Device.Type.SERVER, vendor,
Georgios Katsikas83600982017-05-28 20:41:45 +0200356 hw, sw, serial, new ChassisId(),
357 cpuSet, nicSet, DefaultAnnotations.EMPTY
358 );
359 } catch (URISyntaxException uEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200360 log.error("Failed to create a server device description for: {}",
361 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200362 return null;
363 }
364
365 log.info("Device's {} details sent to the controller", deviceId);
366
367 return desc;
368 }
369
370 @Override
371 public List<PortDescription> discoverPortDetails() {
372 // List of port descriptions to return
373 List<PortDescription> portDescriptions = Lists.newArrayList();
374
375 // Retrieve the device ID
376 DeviceId deviceId = getHandler().data().deviceId();
377 checkNotNull(deviceId, DEVICE_ID_NULL);
378 // .. and object
379 RestServerSBDevice device = null;
380
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200381 // In case this method is called before discoverDeviceDetails,
382 // there is missing information to be gathered.
Georgios Katsikas83600982017-05-28 20:41:45 +0200383 short i = 0;
384 while ((device == null) && (i < DISCOVERY_RETRIES)) {
385 i++;
386
387 try {
388 device = (RestServerSBDevice) getController().getDevice(deviceId);
389 } catch (ClassCastException ccEx) {
390 try {
391 Thread.sleep(1);
392 } catch (InterruptedException intEx) {
393 // Just retry
Georgios Katsikas83600982017-05-28 20:41:45 +0200394 continue;
395 }
396 }
397
398 // No device
399 if (device == null) {
400 // This method will add the device to the RestSBController
401 this.getDeviceDetails(deviceId);
402 }
403 }
404
405 if ((device == null) || (device.nics() == null)) {
406 log.error("No ports available on {}", deviceId);
407 return ImmutableList.copyOf(portDescriptions);
408 }
409
410 // Sorted list of NIC ports
411 Set<NicDevice> nics = new TreeSet(device.nics());
412
413 // Iterate through the NICs of this device to populate the list
414 long portCounter = 0;
415 for (NicDevice nic : nics) {
416 // The port number of this NIC
417 PortNumber portNumber = PortNumber.portNumber(++portCounter);
418
419 // Include the name of this device as an annotation
420 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
421 .set(AnnotationKeys.PORT_NAME, nic.id());
422
423 // Create a port description and add it to the list
424 portDescriptions.add(
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800425 DefaultPortDescription.builder()
426 .withPortNumber(portNumber)
427 .isEnabled(nic.status())
428 .type(nic.portType())
429 .portSpeed(nic.speed())
430 .annotations(annotations.build())
431 .build());
Georgios Katsikas83600982017-05-28 20:41:45 +0200432
Georgios Katsikasd472a322018-07-08 19:58:25 +0200433 log.info("Port discovery on device {}: NIC {} is {} at {} Mbps",
Georgios Katsikas83600982017-05-28 20:41:45 +0200434 deviceId, nic.port(), nic.status() ? "up" : "down",
Georgios Katsikasd472a322018-07-08 19:58:25 +0200435 nic.speed());
Georgios Katsikas83600982017-05-28 20:41:45 +0200436 }
437
438 return ImmutableList.copyOf(portDescriptions);
439 }
440
441 @Override
442 public Collection<PortStatistics> discoverPortStatistics() {
443 // Retrieve the device ID
444 DeviceId deviceId = getHandler().data().deviceId();
445 checkNotNull(deviceId, DEVICE_ID_NULL);
446
447 // Get port statistics for this device
448 return getPortStatistics(deviceId);
449 }
450
451 /**
452 * Query a server to retrieve its port statistics.
453 *
454 * @param deviceId the device ID to be queried
455 * @return list of (per port) PortStatistics
456 */
457 private Collection<PortStatistics> getPortStatistics(DeviceId deviceId) {
458 // List of port statistics to return
459 Collection<PortStatistics> portStats = null;
460
461 // Get global monitoring statistics
462 MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
463 if (monStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200464 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200465 }
466
467 // Filter out the NIC statistics
468 portStats = monStats.nicStatisticsAll();
469 if (portStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200470 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200471 }
472
473 log.debug("Port statistics: {}", portStats.toString());
474
475 return portStats;
476 }
477
478 @Override
479 public Collection<CpuStatistics> discoverCpuStatistics() {
480 // Retrieve the device ID
481 DeviceId deviceId = getHandler().data().deviceId();
482 checkNotNull(deviceId, DEVICE_ID_NULL);
483
484 // Get CPU statistics for this device
485 return getCpuStatistics(deviceId);
486 }
487
488 /**
489 * Query a server to retrieve its CPU statistics.
490 *
491 * @param deviceId the device ID to be queried
492 * @return list of (per core) CpuStatistics
493 */
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200494 public Collection<CpuStatistics> getCpuStatistics(DeviceId deviceId) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200495 // List of port statistics to return
496 Collection<CpuStatistics> cpuStats = null;
497
498 // Get global monitoring statistics
499 MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
500 if (monStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200501 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200502 }
503
504 // Filter out the CPU statistics
505 cpuStats = monStats.cpuStatisticsAll();
506 if (cpuStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200507 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200508 }
509
510 log.debug("CPU statistics: {}", cpuStats.toString());
511
512 return cpuStats;
513 }
514
515 @Override
516 public MonitoringStatistics discoverGlobalMonitoringStatistics() {
517 // Retrieve the device ID
518 DeviceId deviceId = getHandler().data().deviceId();
519 checkNotNull(deviceId, DEVICE_ID_NULL);
520
521 // Get global monitoring statistics for this device
522 return getGlobalMonitoringStatistics(deviceId);
523 }
524
525 /**
526 * Query a server to retrieve its global monitoring statistics.
527 *
528 * @param deviceId the device ID to be queried
529 * @return global monitoring statistics
530 */
531 private MonitoringStatistics getGlobalMonitoringStatistics(DeviceId deviceId) {
532 // Monitoring statistics to return
533 MonitoringStatistics monStats = null;
534
535 RestServerSBDevice device = null;
536 try {
537 device = (RestServerSBDevice) getController().getDevice(deviceId);
538 } catch (ClassCastException ccEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200539 log.error("Failed to retrieve global monitoring statistics from device {}",
540 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200541 return monStats;
542 }
543 checkNotNull(device, DEVICE_NULL);
544
545 // Hit the path that provides the server's global resources
546 InputStream response = null;
547 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200548 response = getController().get(deviceId, GLOBAL_STATS_URL, JSON);
Georgios Katsikas83600982017-05-28 20:41:45 +0200549 } catch (ProcessingException pEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200550 log.error("Failed to retrieve global monitoring statistics from device {}",
551 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200552 return monStats;
553 }
554
555 // Load the JSON into objects
556 ObjectMapper mapper = new ObjectMapper();
557 Map<String, Object> jsonMap = null;
Georgios Katsikas83600982017-05-28 20:41:45 +0200558 ObjectNode objNode = null;
559 try {
560 jsonMap = mapper.readValue(response, Map.class);
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200561 JsonNode jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
Georgios Katsikas83600982017-05-28 20:41:45 +0200562 objNode = (ObjectNode) jsonNode;
563 } catch (IOException ioEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200564 log.error("Failed to retrieve global monitoring statistics from device {}",
565 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200566 return monStats;
567 }
568
569 if (jsonMap == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200570 log.error("Failed to retrieve global monitoring statistics from device {}",
571 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200572 return monStats;
573 }
574
575 // Get high-level CPU statistics
576 int busyCpus = objNode.path(CPU_STATS_BUSY_CPUS).asInt();
577 int freeCpus = objNode.path(CPU_STATS_FREE_CPUS).asInt();
578
579 // Get a list of CPU statistics per core
580 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
581
582 // Get a list of port statistics
583 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
584
585 // Get zero timing statistics
586 TimingStatistics timinsgStats = getZeroTimingStatistics();
587
588 // Ready to construct the grand object
589 DefaultMonitoringStatistics.Builder statsBuilder =
590 DefaultMonitoringStatistics.builder();
591
592 statsBuilder.setDeviceId(deviceId)
593 .setTimingStatistics(timinsgStats)
594 .setCpuStatistics(cpuStats)
595 .setNicStatistics(nicStats)
596 .build();
597
598 monStats = statsBuilder.build();
599
600 log.debug("Global monitoring statistics: {}", monStats.toString());
601
602 return monStats;
603 }
604
605 @Override
606 public MonitoringStatistics discoverMonitoringStatistics(URI tcId) {
607 // Retrieve the device ID
608 DeviceId deviceId = getHandler().data().deviceId();
609 checkNotNull(deviceId, DEVICE_ID_NULL);
610
611 // Get resource-specific monitoring statistics for this device
612 return getMonitoringStatistics(deviceId, tcId);
613 }
614
615 /**
616 * Query a server to retrieve monitoring statistics for a
617 * specific resource (i.e., traffic class).
618 *
619 * @param deviceId the device ID to be queried
620 * @param tcId the ID of the traffic class to be monitored
621 * @return resource-specific monitoring statistics
622 */
623 private MonitoringStatistics getMonitoringStatistics(DeviceId deviceId, URI tcId) {
624 // Monitoring statistics to return
625 MonitoringStatistics monStats = null;
626
627 RestServerSBDevice device = null;
628 try {
629 device = (RestServerSBDevice) getController().getDevice(deviceId);
630 } catch (ClassCastException ccEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200631 log.error("Failed to retrieve monitoring statistics from device {}",
632 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200633 return monStats;
634 }
635 checkNotNull(device, DEVICE_NULL);
636
637 // Create a resource-specific URL
638 String scUrl = SERVICE_CHAINS_STATS_URL + "/" + tcId.toString();
639
640 // Hit the path that provides the server's specific resources
641 InputStream response = null;
642 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200643 response = getController().get(deviceId, scUrl, JSON);
Georgios Katsikas83600982017-05-28 20:41:45 +0200644 } catch (ProcessingException pEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200645 log.error("Failed to retrieve monitoring statistics from device {}",
646 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200647 return monStats;
648 }
649
650 // Load the JSON into objects
651 ObjectMapper mapper = new ObjectMapper();
652 Map<String, Object> jsonMap = null;
653 JsonNode jsonNode = null;
654 ObjectNode objNode = null;
655 try {
656 jsonMap = mapper.readValue(response, Map.class);
657 jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
658 objNode = (ObjectNode) jsonNode;
659 } catch (IOException ioEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200660 log.error("Failed to retrieve monitoring statistics from device {}",
661 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200662 return monStats;
663 }
664
665 if (jsonMap == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200666 log.error("Failed to retrieve monitoring statistics from device {}",
667 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200668 return monStats;
669 }
670
671 // Get the ID of the traffic class
672 String id = get(jsonNode, PARAM_ID);
673
674 // And verify that this is the traffic class we want to monitor
675 if (!id.equals(tcId.toString())) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800676 throw new IllegalStateException(
Georgios Katsikas83600982017-05-28 20:41:45 +0200677 "Failed to retrieve monitoring data for traffic class " + tcId +
678 ". Traffic class ID does not agree."
679 );
680 }
681
682 // Get a list of CPU statistics per core
683 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
684
685 // Get a list of port statistics
686 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
687
688 // Get timing statistics
689 TimingStatistics timinsgStats = parseTimingStatistics(objNode);
690
691 // Ready to construct the grand object
692 DefaultMonitoringStatistics.Builder statsBuilder =
693 DefaultMonitoringStatistics.builder();
694
695 statsBuilder.setDeviceId(deviceId)
696 .setTimingStatistics(timinsgStats)
697 .setCpuStatistics(cpuStats)
698 .setNicStatistics(nicStats)
699 .build();
700
701 monStats = statsBuilder.build();
702
703 log.debug("Monitoring statistics: {}", monStats.toString());
704
705 return monStats;
706 }
707
708 /**
709 * Parse the input JSON object, looking for CPU-related
710 * statistics. Upon success, construct and return a list
711 * of CPU statistics objects.
712 *
713 * @param deviceId the device ID that sent the JSON object
714 * @param objNode input JSON node with CPU statistics information
715 * @return list of (per core) CpuStatistics
716 */
717 private Collection<CpuStatistics> parseCpuStatistics(
718 DeviceId deviceId, JsonNode objNode) {
719 Collection<CpuStatistics> cpuStats = Lists.newArrayList();
720
721 if (objNode == null) {
722 return cpuStats;
723 }
724
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200725 JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
Georgios Katsikas83600982017-05-28 20:41:45 +0200726
727 for (JsonNode cn : cpuNode) {
728 ObjectNode cpuObjNode = (ObjectNode) cn;
729
730 // CPU ID with its load and status
731 int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
732 float cpuLoad = cpuObjNode.path(CPU_PARAM_LOAD).floatValue();
733 boolean isBusy = cpuObjNode.path(CPU_PARAM_STATUS).booleanValue();
734
735 // Incorporate these statistics into an object
736 DefaultCpuStatistics.Builder cpuBuilder =
737 DefaultCpuStatistics.builder();
738
739 cpuBuilder.setDeviceId(deviceId)
740 .setId(cpuId)
741 .setLoad(cpuLoad)
742 .setIsBusy(isBusy)
743 .build();
744
745 // We have statistics for this CPU core
746 cpuStats.add(cpuBuilder.build());
747 }
748
749 return cpuStats;
750 }
751
752 /**
753 * Parse the input JSON object, looking for NIC-related
754 * statistics. Upon success, construct and return a list
755 * of NIC statistics objects.
756 *
757 * @param deviceId the device ID that sent the JSON object
758 * @param objNode input JSON node with NIC statistics information
759 * @return list of (per port) PortStatistics
760 */
761 private Collection<PortStatistics> parseNicStatistics(
762 DeviceId deviceId, JsonNode objNode) {
763 Collection<PortStatistics> nicStats = Lists.newArrayList();
764
765 if (objNode == null) {
766 return nicStats;
767 }
768
769 JsonNode nicNode = objNode.path(PARAM_NICS);
770
771 for (JsonNode nn : nicNode) {
772 ObjectNode nicObjNode = (ObjectNode) nn;
773
774 // All the NIC attributes
775 String nicId = get(nn, NIC_PARAM_ID);
776 int port = Integer.parseInt(nicId.replaceAll("\\D+", ""));
777
778 long rxCount = nicObjNode.path(NIC_STATS_RX_COUNT).asLong();
779 long rxBytes = nicObjNode.path(NIC_STATS_RX_BYTES).asLong();
780 long rxDropped = nicObjNode.path(NIC_STATS_RX_DROPS).asLong();
781 long rxErrors = nicObjNode.path(NIC_STATS_RX_ERRORS).asLong();
782 long txCount = nicObjNode.path(NIC_STATS_TX_COUNT).asLong();
783 long txBytes = nicObjNode.path(NIC_STATS_TX_BYTES).asLong();
784 long txDropped = nicObjNode.path(NIC_STATS_TX_DROPS).asLong();
785 long txErrors = nicObjNode.path(NIC_STATS_TX_ERRORS).asLong();
786
787 // Incorporate these statistics into an object
788 DefaultPortStatistics.Builder nicBuilder =
789 DefaultPortStatistics.builder();
790
791 nicBuilder.setDeviceId(deviceId)
792 .setPort(port)
793 .setPacketsReceived(rxCount)
794 .setPacketsSent(txCount)
795 .setBytesReceived(rxBytes)
796 .setBytesSent(txBytes)
797 .setPacketsRxDropped(rxDropped)
798 .setPacketsRxErrors(rxErrors)
799 .setPacketsTxDropped(txDropped)
800 .setPacketsTxErrors(txErrors)
801 .build();
802
803 // We have statistics for this NIC
804 nicStats.add(nicBuilder.build());
805 }
806
807 return nicStats;
808 }
809
810 /**
811 * Parse the input JSON object, looking for timing-related
812 * statistics. Upon success, construct and return a
813 * timing statistics object.
814 *
815 * @param objNode input JSON node with timing statistics information
816 * @return TimingStatistics object or null
817 */
818 private TimingStatistics parseTimingStatistics(JsonNode objNode) {
819 TimingStatistics timinsgStats = null;
820
821 if (objNode == null) {
822 return timinsgStats;
823 }
824
825 // Get timing statistics
826 JsonNode timingNode = objNode.path(PARAM_TIMING_STATS);
827 ObjectNode timingObjNode = (ObjectNode) timingNode;
828
829 // Time (ns) to parse the controller's deployment instruction
830 long parsingTime = timingObjNode.path(TIMING_PARAM_PARSE).asLong();
831 // Time (ns) to do the deployment
832 long launchingTime = timingObjNode.path(TIMING_PARAM_LAUNCH).asLong();
833 // Total time (ns)
834 long totalTime = parsingTime + launchingTime;
835
836 // Get autoscale timing statistics
837 JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_AUTOSCALE);
838 ObjectNode autoscaleTimingObjNode = (ObjectNode) autoscaleTimingNode;
839
840 // Time (ns) to autoscale a server's load
841 long autoscaleTime = autoscaleTimingObjNode.path(
842 TIMING_PARAM_AUTOSCALE
843 ).asLong();
844
845 DefaultTimingStatistics.Builder timingBuilder =
846 DefaultTimingStatistics.builder();
847
848 timingBuilder.setParsingTime(parsingTime)
849 .setLaunchingTime(launchingTime)
850 .setAutoscaleTime(autoscaleTime)
851 .build();
852
853 return timingBuilder.build();
854 }
855
856 /**
857 * Return a timing statistics object with zero counters.
858 * This is useful when constructing MonitoringStatistics
859 * objects that do not require timers.
860 *
861 * @return TimingStatistics object
862 */
863 private TimingStatistics getZeroTimingStatistics() {
864 DefaultTimingStatistics.Builder zeroTimingBuilder =
865 DefaultTimingStatistics.builder();
866
867 zeroTimingBuilder.setParsingTime(0)
868 .setLaunchingTime(0)
869 .setAutoscaleTime(0)
870 .build();
871
872 return zeroTimingBuilder.build();
873 }
874
875}