blob: 0755bcdbe42e6ab1d2ebd453c42a0fac5da0ab9c [file] [log] [blame]
Georgios Katsikas83600982017-05-28 20:41:45 +02001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.drivers.server;
18
19import org.onosproject.drivers.server.behavior.CpuStatisticsDiscovery;
20import org.onosproject.drivers.server.behavior.MonitoringStatisticsDiscovery;
21import org.onosproject.drivers.server.devices.CpuDevice;
22import org.onosproject.drivers.server.devices.CpuVendor;
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +020023import org.onosproject.drivers.server.devices.nic.NicDevice;
24import org.onosproject.drivers.server.devices.nic.NicRxFilter;
25import org.onosproject.drivers.server.devices.nic.NicRxFilter.RxFilter;
Georgios Katsikas83600982017-05-28 20:41:45 +020026import org.onosproject.drivers.server.devices.ServerDeviceDescription;
27import org.onosproject.drivers.server.devices.RestServerSBDevice;
28import org.onosproject.drivers.server.stats.CpuStatistics;
29import org.onosproject.drivers.server.stats.MonitoringStatistics;
30import org.onosproject.drivers.server.stats.TimingStatistics;
31
32import org.onosproject.drivers.server.impl.devices.DefaultCpuDevice;
33import org.onosproject.drivers.server.impl.devices.DefaultNicDevice;
34import org.onosproject.drivers.server.impl.devices.DefaultRestServerSBDevice;
35import org.onosproject.drivers.server.impl.devices.DefaultServerDeviceDescription;
36import org.onosproject.drivers.server.impl.stats.DefaultCpuStatistics;
37import org.onosproject.drivers.server.impl.stats.DefaultMonitoringStatistics;
38import org.onosproject.drivers.server.impl.stats.DefaultTimingStatistics;
39
40import org.onlab.packet.ChassisId;
41import org.onosproject.net.AnnotationKeys;
42import org.onosproject.net.Device;
43import org.onosproject.net.DeviceId;
44import org.onosproject.net.DefaultAnnotations;
45import org.onosproject.net.behaviour.DevicesDiscovery;
46import org.onosproject.net.device.DeviceDescription;
47import org.onosproject.net.device.DeviceDescriptionDiscovery;
48import org.onosproject.net.device.DefaultPortStatistics;
49import org.onosproject.net.device.DefaultPortDescription;
50import org.onosproject.net.device.PortDescription;
51import org.onosproject.net.device.PortStatistics;
52import org.onosproject.net.device.PortStatisticsDiscovery;
53import org.onosproject.net.Port;
54import org.onosproject.net.PortNumber;
55import org.onosproject.protocol.rest.RestSBDevice;
56import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
57
58import org.slf4j.Logger;
59
60import com.fasterxml.jackson.core.type.TypeReference;
61import com.fasterxml.jackson.databind.JsonNode;
62import com.fasterxml.jackson.databind.ObjectMapper;
63import com.fasterxml.jackson.databind.node.ObjectNode;
64
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +020065import com.google.common.base.Strings;
Georgios Katsikas83600982017-05-28 20:41:45 +020066import com.google.common.collect.Lists;
67import com.google.common.collect.ImmutableList;
68
69import javax.ws.rs.ProcessingException;
70import java.io.InputStream;
71import java.io.IOException;
72import java.net.URI;
73import java.net.URISyntaxException;
74import java.util.ArrayList;
75import java.util.Collection;
76import java.util.Collections;
77import java.util.HashSet;
78import java.util.HashMap;
79import java.util.List;
80import java.util.Map;
81import java.util.Set;
82import java.util.TreeSet;
83
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +020084import static com.google.common.base.Preconditions.checkArgument;
Georgios Katsikas83600982017-05-28 20:41:45 +020085import static com.google.common.base.Preconditions.checkNotNull;
86import static org.slf4j.LoggerFactory.getLogger;
87
88/**
89 * Discovers the device details of
90 * REST-based commodity server devices.
91 */
92public class ServerDevicesDiscovery extends BasicServerDriver
93 implements DevicesDiscovery, DeviceDescriptionDiscovery,
94 PortStatisticsDiscovery, CpuStatisticsDiscovery,
95 MonitoringStatisticsDiscovery {
96
97 private final Logger log = getLogger(getClass());
98
99 /**
100 * Resource endpoints of the server agent (REST server-side).
101 */
102 private static final String RESOURCE_DISCOVERY_URL = BASE_URL + "/resources";
103 private static final String GLOBAL_STATS_URL = BASE_URL + "/stats";
104 private static final String SERVICE_CHAINS_STATS_URL = BASE_URL + "/chains_stats"; // + /ID
105
106 /**
107 * Parameters to be exchanged with the server's agent.
108 */
Georgios Katsikas83600982017-05-28 20:41:45 +0200109 private static final String PARAM_MANUFACTURER = "manufacturer";
110 private static final String PARAM_HW_VENDOR = "hwVersion";
111 private static final String PARAM_SW_VENDOR = "swVersion";
112 private static final String PARAM_SERIAL = "serial";
Georgios Katsikas83600982017-05-28 20:41:45 +0200113 private static final String PARAM_TIMING_STATS = "timing_stats";
114 private static final String PARAM_TIMING_AUTOSCALE = "autoscale_timing_stats";
115
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200116 private static final String NIC_PARAM_NAME = "name";
117 private static final String NIC_PARAM_PORT_INDEX = "index";
Georgios Katsikas83600982017-05-28 20:41:45 +0200118 private static final String NIC_PARAM_PORT_TYPE = "portType";
119 private static final String NIC_PARAM_PORT_TYPE_FIBER = "fiber";
120 private static final String NIC_PARAM_PORT_TYPE_COPPER = "copper";
121 private static final String NIC_PARAM_SPEED = "speed";
122 private static final String NIC_PARAM_STATUS = "status";
123 private static final String NIC_PARAM_HW_ADDR = "hwAddr";
Georgios Katsikas83600982017-05-28 20:41:45 +0200124
125 /**
126 * NIC statistics.
127 */
128 private static final String NIC_STATS_TX_COUNT = "txCount";
129 private static final String NIC_STATS_TX_BYTES = "txBytes";
130 private static final String NIC_STATS_TX_DROPS = "txDropped";
131 private static final String NIC_STATS_TX_ERRORS = "txErrors";
132 private static final String NIC_STATS_RX_COUNT = "rxCount";
133 private static final String NIC_STATS_RX_BYTES = "rxBytes";
134 private static final String NIC_STATS_RX_DROPS = "rxDropped";
135 private static final String NIC_STATS_RX_ERRORS = "rxErrors";
136
137 /**
138 * CPU statistics.
139 */
140 private static final String CPU_PARAM_ID = "id";
141 private static final String CPU_PARAM_VENDOR = "vendor";
142 private static final String CPU_PARAM_FREQUENCY = "frequency";
143 private static final String CPU_PARAM_LOAD = "load";
144 private static final String CPU_PARAM_STATUS = "busy";
145 private static final String CPU_STATS_BUSY_CPUS = "busyCpus";
146 private static final String CPU_STATS_FREE_CPUS = "freeCpus";
147
148 /**
149 * Timing statistics.
150 */
151 private static final String TIMING_PARAM_PARSE = "parse";
152 private static final String TIMING_PARAM_LAUNCH = "launch";
153 private static final String TIMING_PARAM_AUTOSCALE = "autoscale";
154
155 /**
156 * Auxiliary constants.
157 */
158 private static final short DISCOVERY_RETRIES = 3;
159 private static final String CPU_VENDOR_NULL = "Unsupported CPU vendor" +
160 " Choose one in: " + BasicServerDriver.enumTypesToString(CpuVendor.class);
161 private static final String NIC_RX_FILTER_NULL = "Unsupported NIC Rx filter" +
162 " Choose one in: " + BasicServerDriver.enumTypesToString(RxFilter.class);
163
164 /**
165 * Port types that usually appear in commodity servers.
166 */
167 public static final Map<String, Port.Type> PORT_TYPE_MAP =
168 Collections.unmodifiableMap(
169 new HashMap<String, Port.Type>() {
170 {
171 put(NIC_PARAM_PORT_TYPE_FIBER, Port.Type.FIBER);
172 put(NIC_PARAM_PORT_TYPE_COPPER, Port.Type.COPPER);
173 }
174 }
175 );
176
177 /**
178 * Constructs server device discovery.
179 */
180 public ServerDevicesDiscovery() {
181 super();
182 log.debug("Started");
183 }
184
185 @Override
186 public Set<DeviceId> deviceIds() {
187 // Set of devices to return
188 Set<DeviceId> devices = new HashSet<DeviceId>();
189
190 DeviceId deviceId = getHandler().data().deviceId();
191 checkNotNull(deviceId, DEVICE_ID_NULL);
192 devices.add(deviceId);
193
194 return devices;
195 }
196
197 @Override
198 public DeviceDescription deviceDetails(DeviceId deviceId) {
199 return getDeviceDetails(deviceId);
200 }
201
202 @Override
203 public DeviceDescription discoverDeviceDetails() {
204 return getDeviceDetails(null);
205 }
206
207 /**
208 * Query a server to retrieve its features.
209 *
210 * @param deviceId the device ID to be queried
211 * @return a DeviceDescription with the device's features
212 */
213 private DeviceDescription getDeviceDetails(DeviceId deviceId) {
214 // Create a description for this server device
215 ServerDeviceDescription desc = null;
216
217 // Retrieve the device ID, if null given
218 if (deviceId == null) {
219 deviceId = getHandler().data().deviceId();
220 checkNotNull(deviceId, DEVICE_ID_NULL);
221 }
222
223 // Get the device
224 RestSBDevice device = getController().getDevice(deviceId);
225 checkNotNull(device, DEVICE_NULL);
226
227 // Hit the path that provides the server's resources
228 InputStream response = null;
229 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200230 response = getController().get(deviceId, RESOURCE_DISCOVERY_URL, JSON);
Georgios Katsikas83600982017-05-28 20:41:45 +0200231 } catch (ProcessingException pEx) {
232 log.error("Failed to discover the device details of: {}", deviceId);
233 return desc;
234 }
235
236 // Load the JSON into objects
237 ObjectMapper mapper = new ObjectMapper();
238 Map<String, Object> jsonMap = null;
239 JsonNode jsonNode = null;
240 ObjectNode objNode = null;
241 try {
242 jsonMap = mapper.readValue(response, Map.class);
243 jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
244 objNode = (ObjectNode) jsonNode;
245 } catch (IOException ioEx) {
246 log.error("Failed to discover the device details of: {}", deviceId);
247 return desc;
248 }
249
250 if (jsonMap == null) {
251 log.error("Failed to discover the device details of: {}", deviceId);
252 return desc;
253 }
254
255 // Get all the attributes
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200256 String id = get(jsonNode, BasicServerDriver.PARAM_ID);
Georgios Katsikas83600982017-05-28 20:41:45 +0200257 String vendor = get(jsonNode, PARAM_MANUFACTURER);
258 String hw = get(jsonNode, PARAM_HW_VENDOR);
259 String sw = get(jsonNode, PARAM_SW_VENDOR);
260 String serial = get(jsonNode, PARAM_SERIAL);
261
262 // CPUs are composite attributes
263 Set<CpuDevice> cpuSet = new HashSet<CpuDevice>();
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200264 JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
Georgios Katsikas83600982017-05-28 20:41:45 +0200265
266 // Construct CPU objects
267 for (JsonNode cn : cpuNode) {
268 ObjectNode cpuObjNode = (ObjectNode) cn;
269
270 // All the CPU attributes
271 int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
272 String cpuVendorStr = get(cn, CPU_PARAM_VENDOR);
273 long cpuFrequency = cpuObjNode.path(CPU_PARAM_FREQUENCY).asLong();
274
275 // Verify that this is a valid vendor
276 CpuVendor cpuVendor = CpuVendor.getByName(cpuVendorStr);
277 checkNotNull(cpuVendor, CPU_VENDOR_NULL);
278
279 // Construct a CPU device
280 CpuDevice cpu = new DefaultCpuDevice(cpuId, cpuVendor, cpuFrequency);
281
282 // Add it to the set
283 cpuSet.add(cpu);
284 }
285
286 // NICs are composite attributes too
287 Set<NicDevice> nicSet = new HashSet<NicDevice>();
288 JsonNode nicNode = objNode.path(PARAM_NICS);
289
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200290 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
291
Georgios Katsikas83600982017-05-28 20:41:45 +0200292 // Construct NIC objects
293 for (JsonNode nn : nicNode) {
294 ObjectNode nicObjNode = (ObjectNode) nn;
295
296 // All the NIC attributes
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200297 String nicName = get(nn, NIC_PARAM_NAME);
298 long nicIndex = nicObjNode.path(NIC_PARAM_PORT_INDEX).asLong();
Georgios Katsikas83600982017-05-28 20:41:45 +0200299 long speed = nicObjNode.path(NIC_PARAM_SPEED).asLong();
300 String portTypeStr = get(nn, NIC_PARAM_PORT_TYPE);
301 Port.Type portType = PORT_TYPE_MAP.get(portTypeStr);
302 if (portType == null) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800303 throw new IllegalArgumentException(
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200304 portTypeStr + " is not a valid port type for NIC " + nicName);
Georgios Katsikas83600982017-05-28 20:41:45 +0200305 }
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 Katsikas80e0b9f2018-07-21 20:29:18 +0200311 "The Rx filters of NIC " + nicName + " are not reported");
Georgios Katsikas83600982017-05-28 20:41:45 +0200312 }
313
314 // Convert the JSON list into an array of strings
315 List<String> rxFilters = null;
316 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200317 rxFilters = mapper.readValue(tagNode.traverse(),
318 new TypeReference<ArrayList<String>>() { });
Georgios Katsikas83600982017-05-28 20:41:45 +0200319 } catch (IOException ioEx) {
320 continue;
321 }
322
323 // Parse the array of strings and create an RxFilter object
324 NicRxFilter rxFilterMechanism = new NicRxFilter();
325 for (String s : rxFilters) {
326 // Verify that this is a valid Rx filter
327 RxFilter rf = RxFilter.getByName(s);
328 checkNotNull(rf, NIC_RX_FILTER_NULL);
329
330 rxFilterMechanism.addRxFilter(rf);
331 }
332
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200333 // Store NIC name to number mapping as an annotation
334 annotations.set(nicName, Long.toString(nicIndex));
335
Georgios Katsikas83600982017-05-28 20:41:45 +0200336 // Construct a NIC device for this server
337 NicDevice nic = new DefaultNicDevice(
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200338 nicName, nicIndex, portType, speed, status, hwAddr, rxFilterMechanism);
Georgios Katsikas83600982017-05-28 20:41:45 +0200339
340 // Add it to the set
341 nicSet.add(nic);
342 }
343
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200344 // Construct a complete server device object.
345 // Lists of NICs and CPUs extend the information
346 // already in RestSBDevice (parent class).
Georgios Katsikas83600982017-05-28 20:41:45 +0200347 RestServerSBDevice dev = new DefaultRestServerSBDevice(
348 device.ip(), device.port(), device.username(),
349 device.password(), device.protocol(), device.url(),
350 device.isActive(), device.testUrl().toString(),
351 vendor, hw, sw, AuthenticationScheme.BASIC, "",
352 cpuSet, nicSet
353 );
354 checkNotNull(dev, DEVICE_NULL);
355
356 // Updates the controller with the complete device information
357 getController().removeDevice(deviceId);
358 getController().addDevice((RestSBDevice) dev);
359
Georgios Katsikas83600982017-05-28 20:41:45 +0200360 try {
361 desc = new DefaultServerDeviceDescription(
Georgios Katsikas40ecef32018-07-03 11:17:44 +0200362 new URI(id), Device.Type.SERVER, vendor,
Georgios Katsikas83600982017-05-28 20:41:45 +0200363 hw, sw, serial, new ChassisId(),
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200364 cpuSet, nicSet, annotations.build()
Georgios Katsikas83600982017-05-28 20:41:45 +0200365 );
366 } catch (URISyntaxException uEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200367 log.error("Failed to create a server device description for: {}",
368 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200369 return null;
370 }
371
372 log.info("Device's {} details sent to the controller", deviceId);
373
374 return desc;
375 }
376
377 @Override
378 public List<PortDescription> discoverPortDetails() {
379 // List of port descriptions to return
380 List<PortDescription> portDescriptions = Lists.newArrayList();
381
382 // Retrieve the device ID
383 DeviceId deviceId = getHandler().data().deviceId();
384 checkNotNull(deviceId, DEVICE_ID_NULL);
385 // .. and object
386 RestServerSBDevice device = null;
387
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200388 // In case this method is called before discoverDeviceDetails,
389 // there is missing information to be gathered.
Georgios Katsikas83600982017-05-28 20:41:45 +0200390 short i = 0;
391 while ((device == null) && (i < DISCOVERY_RETRIES)) {
392 i++;
393
394 try {
395 device = (RestServerSBDevice) getController().getDevice(deviceId);
396 } catch (ClassCastException ccEx) {
397 try {
398 Thread.sleep(1);
399 } catch (InterruptedException intEx) {
400 // Just retry
Georgios Katsikas83600982017-05-28 20:41:45 +0200401 continue;
402 }
403 }
404
405 // No device
406 if (device == null) {
407 // This method will add the device to the RestSBController
408 this.getDeviceDetails(deviceId);
409 }
410 }
411
412 if ((device == null) || (device.nics() == null)) {
413 log.error("No ports available on {}", deviceId);
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200414 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200415 }
416
417 // Sorted list of NIC ports
418 Set<NicDevice> nics = new TreeSet(device.nics());
419
420 // Iterate through the NICs of this device to populate the list
Georgios Katsikas83600982017-05-28 20:41:45 +0200421 for (NicDevice nic : nics) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200422 // Include the name of this device as an annotation
423 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200424 .set(AnnotationKeys.PORT_NAME, nic.name());
Georgios Katsikas83600982017-05-28 20:41:45 +0200425
426 // Create a port description and add it to the list
427 portDescriptions.add(
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800428 DefaultPortDescription.builder()
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200429 .withPortNumber(PortNumber.portNumber(nic.portNumber(), nic.name()))
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800430 .isEnabled(nic.status())
431 .type(nic.portType())
432 .portSpeed(nic.speed())
433 .annotations(annotations.build())
434 .build());
Georgios Katsikas83600982017-05-28 20:41:45 +0200435
Georgios Katsikasd472a322018-07-08 19:58:25 +0200436 log.info("Port discovery on device {}: NIC {} is {} at {} Mbps",
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200437 deviceId, nic.portNumber(), nic.status() ? "up" : "down",
Georgios Katsikasd472a322018-07-08 19:58:25 +0200438 nic.speed());
Georgios Katsikas83600982017-05-28 20:41:45 +0200439 }
440
441 return ImmutableList.copyOf(portDescriptions);
442 }
443
444 @Override
445 public Collection<PortStatistics> discoverPortStatistics() {
446 // Retrieve the device ID
447 DeviceId deviceId = getHandler().data().deviceId();
448 checkNotNull(deviceId, DEVICE_ID_NULL);
449
450 // Get port statistics for this device
451 return getPortStatistics(deviceId);
452 }
453
454 /**
455 * Query a server to retrieve its port statistics.
456 *
457 * @param deviceId the device ID to be queried
458 * @return list of (per port) PortStatistics
459 */
460 private Collection<PortStatistics> getPortStatistics(DeviceId deviceId) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200461 // 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
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200468 Collection<PortStatistics> portStats = monStats.nicStatisticsAll();
Georgios Katsikas83600982017-05-28 20:41:45 +0200469 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 // Get global monitoring statistics
496 MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
497 if (monStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200498 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200499 }
500
501 // Filter out the CPU statistics
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200502 Collection<CpuStatistics> cpuStats = monStats.cpuStatisticsAll();
Georgios Katsikas83600982017-05-28 20:41:45 +0200503 if (cpuStats == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200504 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200505 }
506
507 log.debug("CPU statistics: {}", cpuStats.toString());
508
509 return cpuStats;
510 }
511
512 @Override
513 public MonitoringStatistics discoverGlobalMonitoringStatistics() {
514 // Retrieve the device ID
515 DeviceId deviceId = getHandler().data().deviceId();
516 checkNotNull(deviceId, DEVICE_ID_NULL);
517
518 // Get global monitoring statistics for this device
519 return getGlobalMonitoringStatistics(deviceId);
520 }
521
522 /**
523 * Query a server to retrieve its global monitoring statistics.
524 *
525 * @param deviceId the device ID to be queried
526 * @return global monitoring statistics
527 */
528 private MonitoringStatistics getGlobalMonitoringStatistics(DeviceId deviceId) {
529 // Monitoring statistics to return
530 MonitoringStatistics monStats = null;
531
532 RestServerSBDevice device = null;
533 try {
534 device = (RestServerSBDevice) getController().getDevice(deviceId);
535 } catch (ClassCastException ccEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200536 log.error("Failed to retrieve global monitoring statistics from device {}",
537 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200538 return monStats;
539 }
540 checkNotNull(device, DEVICE_NULL);
541
542 // Hit the path that provides the server's global resources
543 InputStream response = null;
544 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200545 response = getController().get(deviceId, GLOBAL_STATS_URL, JSON);
Georgios Katsikas83600982017-05-28 20:41:45 +0200546 } catch (ProcessingException pEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200547 log.error("Failed to retrieve global monitoring statistics from device {}",
548 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200549 return monStats;
550 }
551
552 // Load the JSON into objects
553 ObjectMapper mapper = new ObjectMapper();
554 Map<String, Object> jsonMap = null;
Georgios Katsikas83600982017-05-28 20:41:45 +0200555 ObjectNode objNode = null;
556 try {
557 jsonMap = mapper.readValue(response, Map.class);
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200558 JsonNode jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
Georgios Katsikas83600982017-05-28 20:41:45 +0200559 objNode = (ObjectNode) jsonNode;
560 } catch (IOException ioEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200561 log.error("Failed to retrieve global monitoring statistics from device {}",
562 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200563 return monStats;
564 }
565
566 if (jsonMap == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200567 log.error("Failed to retrieve global monitoring statistics from device {}",
568 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200569 return monStats;
570 }
571
572 // Get high-level CPU statistics
573 int busyCpus = objNode.path(CPU_STATS_BUSY_CPUS).asInt();
574 int freeCpus = objNode.path(CPU_STATS_FREE_CPUS).asInt();
575
576 // Get a list of CPU statistics per core
577 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
578
579 // Get a list of port statistics
580 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
581
582 // Get zero timing statistics
583 TimingStatistics timinsgStats = getZeroTimingStatistics();
584
585 // Ready to construct the grand object
586 DefaultMonitoringStatistics.Builder statsBuilder =
587 DefaultMonitoringStatistics.builder();
588
589 statsBuilder.setDeviceId(deviceId)
590 .setTimingStatistics(timinsgStats)
591 .setCpuStatistics(cpuStats)
592 .setNicStatistics(nicStats)
593 .build();
594
595 monStats = statsBuilder.build();
596
597 log.debug("Global monitoring statistics: {}", monStats.toString());
598
599 return monStats;
600 }
601
602 @Override
603 public MonitoringStatistics discoverMonitoringStatistics(URI tcId) {
604 // Retrieve the device ID
605 DeviceId deviceId = getHandler().data().deviceId();
606 checkNotNull(deviceId, DEVICE_ID_NULL);
607
608 // Get resource-specific monitoring statistics for this device
609 return getMonitoringStatistics(deviceId, tcId);
610 }
611
612 /**
613 * Query a server to retrieve monitoring statistics for a
614 * specific resource (i.e., traffic class).
615 *
616 * @param deviceId the device ID to be queried
617 * @param tcId the ID of the traffic class to be monitored
618 * @return resource-specific monitoring statistics
619 */
620 private MonitoringStatistics getMonitoringStatistics(DeviceId deviceId, URI tcId) {
621 // Monitoring statistics to return
622 MonitoringStatistics monStats = null;
623
624 RestServerSBDevice device = null;
625 try {
626 device = (RestServerSBDevice) getController().getDevice(deviceId);
627 } catch (ClassCastException ccEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200628 log.error("Failed to retrieve monitoring statistics from device {}",
629 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200630 return monStats;
631 }
632 checkNotNull(device, DEVICE_NULL);
633
634 // Create a resource-specific URL
635 String scUrl = SERVICE_CHAINS_STATS_URL + "/" + tcId.toString();
636
637 // Hit the path that provides the server's specific resources
638 InputStream response = null;
639 try {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200640 response = getController().get(deviceId, scUrl, JSON);
Georgios Katsikas83600982017-05-28 20:41:45 +0200641 } catch (ProcessingException pEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200642 log.error("Failed to retrieve monitoring statistics from device {}",
643 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200644 return monStats;
645 }
646
647 // Load the JSON into objects
648 ObjectMapper mapper = new ObjectMapper();
649 Map<String, Object> jsonMap = null;
650 JsonNode jsonNode = null;
651 ObjectNode objNode = null;
652 try {
653 jsonMap = mapper.readValue(response, Map.class);
654 jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
655 objNode = (ObjectNode) jsonNode;
656 } catch (IOException ioEx) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200657 log.error("Failed to retrieve monitoring statistics from device {}",
658 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200659 return monStats;
660 }
661
662 if (jsonMap == null) {
Georgios Katsikasd472a322018-07-08 19:58:25 +0200663 log.error("Failed to retrieve monitoring statistics from device {}",
664 deviceId);
Georgios Katsikas83600982017-05-28 20:41:45 +0200665 return monStats;
666 }
667
668 // Get the ID of the traffic class
669 String id = get(jsonNode, PARAM_ID);
670
671 // And verify that this is the traffic class we want to monitor
672 if (!id.equals(tcId.toString())) {
Ray Milkey067c44b2018-02-26 12:48:23 -0800673 throw new IllegalStateException(
Georgios Katsikas83600982017-05-28 20:41:45 +0200674 "Failed to retrieve monitoring data for traffic class " + tcId +
675 ". Traffic class ID does not agree."
676 );
677 }
678
679 // Get a list of CPU statistics per core
680 Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
681
682 // Get a list of port statistics
683 Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
684
685 // Get timing statistics
686 TimingStatistics timinsgStats = parseTimingStatistics(objNode);
687
688 // Ready to construct the grand object
689 DefaultMonitoringStatistics.Builder statsBuilder =
690 DefaultMonitoringStatistics.builder();
691
692 statsBuilder.setDeviceId(deviceId)
693 .setTimingStatistics(timinsgStats)
694 .setCpuStatistics(cpuStats)
695 .setNicStatistics(nicStats)
696 .build();
697
698 monStats = statsBuilder.build();
699
700 log.debug("Monitoring statistics: {}", monStats.toString());
701
702 return monStats;
703 }
704
705 /**
706 * Parse the input JSON object, looking for CPU-related
707 * statistics. Upon success, construct and return a list
708 * of CPU statistics objects.
709 *
710 * @param deviceId the device ID that sent the JSON object
711 * @param objNode input JSON node with CPU statistics information
712 * @return list of (per core) CpuStatistics
713 */
714 private Collection<CpuStatistics> parseCpuStatistics(
715 DeviceId deviceId, JsonNode objNode) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200716 if (objNode == null) {
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200717 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200718 }
719
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200720 Collection<CpuStatistics> cpuStats = Lists.newArrayList();
721
Georgios Katsikas2ebd8a02018-06-27 18:32:50 +0200722 JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
Georgios Katsikas83600982017-05-28 20:41:45 +0200723
724 for (JsonNode cn : cpuNode) {
725 ObjectNode cpuObjNode = (ObjectNode) cn;
726
727 // CPU ID with its load and status
728 int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
729 float cpuLoad = cpuObjNode.path(CPU_PARAM_LOAD).floatValue();
730 boolean isBusy = cpuObjNode.path(CPU_PARAM_STATUS).booleanValue();
731
732 // Incorporate these statistics into an object
733 DefaultCpuStatistics.Builder cpuBuilder =
734 DefaultCpuStatistics.builder();
735
736 cpuBuilder.setDeviceId(deviceId)
737 .setId(cpuId)
738 .setLoad(cpuLoad)
739 .setIsBusy(isBusy)
740 .build();
741
742 // We have statistics for this CPU core
743 cpuStats.add(cpuBuilder.build());
744 }
745
746 return cpuStats;
747 }
748
749 /**
750 * Parse the input JSON object, looking for NIC-related
751 * statistics. Upon success, construct and return a list
752 * of NIC statistics objects.
753 *
754 * @param deviceId the device ID that sent the JSON object
755 * @param objNode input JSON node with NIC statistics information
756 * @return list of (per port) PortStatistics
757 */
758 private Collection<PortStatistics> parseNicStatistics(
759 DeviceId deviceId, JsonNode objNode) {
Georgios Katsikas83600982017-05-28 20:41:45 +0200760 if (objNode == null) {
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200761 return Collections.EMPTY_LIST;
Georgios Katsikas83600982017-05-28 20:41:45 +0200762 }
763
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200764 RestServerSBDevice device = null;
765 try {
766 device = (RestServerSBDevice) getController().getDevice(deviceId);
767 } catch (ClassCastException ccEx) {
768 return Collections.EMPTY_LIST;
769 }
770 checkNotNull(device, DEVICE_NULL);
771
772 Collection<PortStatistics> nicStats = Lists.newArrayList();
773
Georgios Katsikas83600982017-05-28 20:41:45 +0200774 JsonNode nicNode = objNode.path(PARAM_NICS);
775
776 for (JsonNode nn : nicNode) {
777 ObjectNode nicObjNode = (ObjectNode) nn;
778
779 // All the NIC attributes
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200780 String nicName = get(nn, NIC_PARAM_NAME);
781 checkArgument(!Strings.isNullOrEmpty(nicName), "NIC name is empty or NULL");
782
783 long portNumber = device.portNumberFromName(nicName);
784 checkArgument(portNumber >= 0, "Unknown port ID " + portNumber + " for NIC " + nicName);
Georgios Katsikas83600982017-05-28 20:41:45 +0200785
786 long rxCount = nicObjNode.path(NIC_STATS_RX_COUNT).asLong();
787 long rxBytes = nicObjNode.path(NIC_STATS_RX_BYTES).asLong();
788 long rxDropped = nicObjNode.path(NIC_STATS_RX_DROPS).asLong();
789 long rxErrors = nicObjNode.path(NIC_STATS_RX_ERRORS).asLong();
790 long txCount = nicObjNode.path(NIC_STATS_TX_COUNT).asLong();
791 long txBytes = nicObjNode.path(NIC_STATS_TX_BYTES).asLong();
792 long txDropped = nicObjNode.path(NIC_STATS_TX_DROPS).asLong();
793 long txErrors = nicObjNode.path(NIC_STATS_TX_ERRORS).asLong();
794
795 // Incorporate these statistics into an object
796 DefaultPortStatistics.Builder nicBuilder =
797 DefaultPortStatistics.builder();
798
799 nicBuilder.setDeviceId(deviceId)
Georgios Katsikas80e0b9f2018-07-21 20:29:18 +0200800 .setPort((int) portNumber)
Georgios Katsikas83600982017-05-28 20:41:45 +0200801 .setPacketsReceived(rxCount)
802 .setPacketsSent(txCount)
803 .setBytesReceived(rxBytes)
804 .setBytesSent(txBytes)
805 .setPacketsRxDropped(rxDropped)
806 .setPacketsRxErrors(rxErrors)
807 .setPacketsTxDropped(txDropped)
808 .setPacketsTxErrors(txErrors)
809 .build();
810
811 // We have statistics for this NIC
812 nicStats.add(nicBuilder.build());
813 }
814
815 return nicStats;
816 }
817
818 /**
819 * Parse the input JSON object, looking for timing-related
820 * statistics. Upon success, construct and return a
821 * timing statistics object.
822 *
823 * @param objNode input JSON node with timing statistics information
824 * @return TimingStatistics object or null
825 */
826 private TimingStatistics parseTimingStatistics(JsonNode objNode) {
827 TimingStatistics timinsgStats = null;
828
829 if (objNode == null) {
830 return timinsgStats;
831 }
832
833 // Get timing statistics
834 JsonNode timingNode = objNode.path(PARAM_TIMING_STATS);
835 ObjectNode timingObjNode = (ObjectNode) timingNode;
836
837 // Time (ns) to parse the controller's deployment instruction
838 long parsingTime = timingObjNode.path(TIMING_PARAM_PARSE).asLong();
839 // Time (ns) to do the deployment
840 long launchingTime = timingObjNode.path(TIMING_PARAM_LAUNCH).asLong();
841 // Total time (ns)
842 long totalTime = parsingTime + launchingTime;
843
844 // Get autoscale timing statistics
845 JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_AUTOSCALE);
846 ObjectNode autoscaleTimingObjNode = (ObjectNode) autoscaleTimingNode;
847
848 // Time (ns) to autoscale a server's load
849 long autoscaleTime = autoscaleTimingObjNode.path(
850 TIMING_PARAM_AUTOSCALE
851 ).asLong();
852
853 DefaultTimingStatistics.Builder timingBuilder =
854 DefaultTimingStatistics.builder();
855
856 timingBuilder.setParsingTime(parsingTime)
857 .setLaunchingTime(launchingTime)
858 .setAutoscaleTime(autoscaleTime)
859 .build();
860
861 return timingBuilder.build();
862 }
863
864 /**
865 * Return a timing statistics object with zero counters.
866 * This is useful when constructing MonitoringStatistics
867 * objects that do not require timers.
868 *
869 * @return TimingStatistics object
870 */
871 private TimingStatistics getZeroTimingStatistics() {
872 DefaultTimingStatistics.Builder zeroTimingBuilder =
873 DefaultTimingStatistics.builder();
874
875 zeroTimingBuilder.setParsingTime(0)
876 .setLaunchingTime(0)
877 .setAutoscaleTime(0)
878 .build();
879
880 return zeroTimingBuilder.build();
881 }
882
883}