blob: 8506a4001948ece0603d89de18c1e41aebd92dd5 [file] [log] [blame]
Carmelo Casconefa421582018-09-13 10:05:57 -07001/*
2 * Copyright 2015-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 */
16package org.onosproject.inbandtelemetry.impl;
17
18import com.google.common.collect.Maps;
19import com.google.common.util.concurrent.Striped;
Carmelo Casconefa421582018-09-13 10:05:57 -070020import org.onlab.util.KryoNamespace;
21import org.onlab.util.SharedScheduledExecutors;
22import org.onosproject.core.ApplicationId;
23import org.onosproject.core.CoreService;
Yi Tseng0f1ffd12020-09-18 11:10:47 -070024import org.onosproject.net.behaviour.inbandtelemetry.IntReportConfig;
Carmelo Casconedefc74e2020-07-17 15:27:02 -070025import org.onosproject.net.behaviour.inbandtelemetry.IntMetadataType;
26import org.onosproject.net.behaviour.inbandtelemetry.IntDeviceConfig;
Carmelo Casconefa421582018-09-13 10:05:57 -070027import org.onosproject.inbandtelemetry.api.IntIntent;
28import org.onosproject.inbandtelemetry.api.IntIntentId;
Carmelo Casconedefc74e2020-07-17 15:27:02 -070029import org.onosproject.net.behaviour.inbandtelemetry.IntObjective;
30import org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable;
Carmelo Casconefa421582018-09-13 10:05:57 -070031import org.onosproject.inbandtelemetry.api.IntService;
32import org.onosproject.mastership.MastershipService;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.Device;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.MastershipRole;
37import org.onosproject.net.PortNumber;
Yi Tseng0f1ffd12020-09-18 11:10:47 -070038import org.onosproject.net.config.ConfigFactory;
39import org.onosproject.net.config.NetworkConfigEvent;
40import org.onosproject.net.config.NetworkConfigListener;
41import org.onosproject.net.config.NetworkConfigRegistry;
42import org.onosproject.net.config.NetworkConfigService;
43import org.onosproject.net.config.basics.SubjectFactories;
Carmelo Casconefa421582018-09-13 10:05:57 -070044import org.onosproject.net.device.DeviceEvent;
45import org.onosproject.net.device.DeviceListener;
46import org.onosproject.net.device.DeviceService;
47import org.onosproject.net.host.HostEvent;
48import org.onosproject.net.host.HostListener;
49import org.onosproject.net.host.HostService;
50import org.onosproject.store.serializers.KryoNamespaces;
51import org.onosproject.store.service.AtomicIdGenerator;
52import org.onosproject.store.service.AtomicValue;
53import org.onosproject.store.service.AtomicValueEvent;
54import org.onosproject.store.service.AtomicValueEventListener;
55import org.onosproject.store.service.ConsistentMap;
56import org.onosproject.store.service.MapEvent;
57import org.onosproject.store.service.MapEventListener;
58import org.onosproject.store.service.Serializer;
59import org.onosproject.store.service.StorageService;
60import org.onosproject.store.service.Versioned;
Ray Milkeydb57f1c2018-10-09 10:39:29 -070061import org.osgi.service.component.annotations.Activate;
62import org.osgi.service.component.annotations.Component;
63import org.osgi.service.component.annotations.Deactivate;
64import org.osgi.service.component.annotations.Reference;
65import org.osgi.service.component.annotations.ReferenceCardinality;
Carmelo Casconefa421582018-09-13 10:05:57 -070066import org.slf4j.Logger;
67
68import java.util.Collection;
69import java.util.Map;
70import java.util.Optional;
71import java.util.Set;
72import java.util.concurrent.ConcurrentMap;
73import java.util.concurrent.ExecutionException;
74import java.util.concurrent.ScheduledFuture;
75import java.util.concurrent.TimeUnit;
76import java.util.concurrent.TimeoutException;
77import java.util.concurrent.locks.Lock;
78import java.util.stream.Collectors;
79
80import static com.google.common.base.Preconditions.checkNotNull;
81import static org.slf4j.LoggerFactory.getLogger;
82
83/**
84 * Simple implementation of IntService, for controlling INT-capable pipelines.
85 * <p>
86 * All INT intents are converted to an equivalent INT objective and applied to
87 * all SOURCE_SINK devices. A device is deemed SOURCE_SINK if it has at least
88 * one host attached.
89 * <p>
90 * The implementation listens for different types of events and when required it
91 * configures a device by cleaning-up any previous state and applying the new
92 * one.
93 */
Ray Milkeydb57f1c2018-10-09 10:39:29 -070094@Component(immediate = true, service = IntService.class)
Carmelo Casconefa421582018-09-13 10:05:57 -070095public class SimpleIntManager implements IntService {
96
97 private final Logger log = getLogger(getClass());
98
99 private static final int CONFIG_EVENT_DELAY = 5; // Seconds.
100
101 private static final String APP_NAME = "org.onosproject.inbandtelemetry";
102
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconefa421582018-09-13 10:05:57 -0700104 private CoreService coreService;
105
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconefa421582018-09-13 10:05:57 -0700107 private DeviceService deviceService;
108
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconefa421582018-09-13 10:05:57 -0700110 private StorageService storageService;
111
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconefa421582018-09-13 10:05:57 -0700113 private MastershipService mastershipService;
114
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Carmelo Casconefa421582018-09-13 10:05:57 -0700116 private HostService hostService;
117
Yi Tseng0f1ffd12020-09-18 11:10:47 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
119 private NetworkConfigService netcfgService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
122 private NetworkConfigRegistry netcfgRegistry;
123
Carmelo Casconefa421582018-09-13 10:05:57 -0700124 private final Striped<Lock> deviceLocks = Striped.lock(10);
125
126 private final ConcurrentMap<DeviceId, ScheduledFuture<?>> scheduledDeviceTasks = Maps.newConcurrentMap();
127
128 // Distributed state.
129 private ConsistentMap<IntIntentId, IntIntent> intentMap;
130 private ConsistentMap<DeviceId, Long> devicesToConfigure;
Carmelo Casconedefc74e2020-07-17 15:27:02 -0700131 private AtomicValue<IntDeviceConfig> intConfig;
Carmelo Casconefa421582018-09-13 10:05:57 -0700132 private AtomicValue<Boolean> intStarted;
133 private AtomicIdGenerator intentIds;
134
135 // Event listeners.
136 private final InternalHostListener hostListener = new InternalHostListener();
137 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
138 private final InternalIntentMapListener intentMapListener = new InternalIntentMapListener();
139 private final InternalIntConfigListener intConfigListener = new InternalIntConfigListener();
140 private final InternalIntStartedListener intStartedListener = new InternalIntStartedListener();
141 private final InternalDeviceToConfigureListener devicesToConfigureListener =
142 new InternalDeviceToConfigureListener();
Yi Tseng0f1ffd12020-09-18 11:10:47 -0700143 private final NetworkConfigListener appConfigListener = new IntAppConfigListener();
144
145 private final ConfigFactory<ApplicationId, IntReportConfig> intAppConfigFactory =
146 new ConfigFactory<>(SubjectFactories.APP_SUBJECT_FACTORY,
147 IntReportConfig.class, "report") {
148 @Override
149 public IntReportConfig createConfig() {
150 return new IntReportConfig();
151 }
152 };
Carmelo Casconefa421582018-09-13 10:05:57 -0700153
154 @Activate
155 public void activate() {
156
157 final ApplicationId appId = coreService.registerApplication(APP_NAME);
158
159 KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
160 .register(KryoNamespaces.API)
161 .register(IntIntent.class)
162 .register(IntIntentId.class)
163 .register(IntDeviceRole.class)
164 .register(IntIntent.IntHeaderType.class)
Carmelo Casconedefc74e2020-07-17 15:27:02 -0700165 .register(IntMetadataType.class)
Carmelo Casconefa421582018-09-13 10:05:57 -0700166 .register(IntIntent.IntReportType.class)
167 .register(IntIntent.TelemetryMode.class)
Carmelo Casconedefc74e2020-07-17 15:27:02 -0700168 .register(IntDeviceConfig.class)
169 .register(IntDeviceConfig.TelemetrySpec.class);
Carmelo Casconefa421582018-09-13 10:05:57 -0700170
171 devicesToConfigure = storageService.<DeviceId, Long>consistentMapBuilder()
172 .withSerializer(Serializer.using(serializer.build()))
173 .withName("onos-int-devices-to-configure")
174 .withApplicationId(appId)
175 .withPurgeOnUninstall()
176 .build();
177 devicesToConfigure.addListener(devicesToConfigureListener);
178
179 intentMap = storageService.<IntIntentId, IntIntent>consistentMapBuilder()
180 .withSerializer(Serializer.using(serializer.build()))
181 .withName("onos-int-intents")
182 .withApplicationId(appId)
183 .withPurgeOnUninstall()
184 .build();
185 intentMap.addListener(intentMapListener);
186
187 intStarted = storageService.<Boolean>atomicValueBuilder()
188 .withSerializer(Serializer.using(serializer.build()))
189 .withName("onos-int-started")
190 .withApplicationId(appId)
191 .build()
192 .asAtomicValue();
193 intStarted.addListener(intStartedListener);
194
Carmelo Casconedefc74e2020-07-17 15:27:02 -0700195 intConfig = storageService.<IntDeviceConfig>atomicValueBuilder()
Carmelo Casconefa421582018-09-13 10:05:57 -0700196 .withSerializer(Serializer.using(serializer.build()))
197 .withName("onos-int-config")
198 .withApplicationId(appId)
199 .build()
200 .asAtomicValue();
201 intConfig.addListener(intConfigListener);
202
203 intentIds = storageService.getAtomicIdGenerator("int-intent-id-generator");
204
205 // Bootstrap config for already existing devices.
206 triggerAllDeviceConfigure();
207
208 hostService.addListener(hostListener);
209 deviceService.addListener(deviceListener);
210
Yi Tseng0f1ffd12020-09-18 11:10:47 -0700211 netcfgRegistry.registerConfigFactory(intAppConfigFactory);
212 netcfgService.addListener(appConfigListener);
Yi Tseng930b0cd2020-10-04 22:33:22 -0700213 // Initialize the INT report
214 IntReportConfig reportConfig = netcfgService.getConfig(appId, IntReportConfig.class);
215 if (reportConfig != null) {
216 IntDeviceConfig intDeviceConfig = IntDeviceConfig.builder()
217 .withMinFlowHopLatencyChangeNs(reportConfig.minFlowHopLatencyChangeNs())
218 .withCollectorPort(reportConfig.collectorPort())
219 .withCollectorIp(reportConfig.collectorIp())
220 .enabled(true)
221 .build();
222 setConfig(intDeviceConfig);
223 }
Yi Tseng0f1ffd12020-09-18 11:10:47 -0700224
Carmelo Casconefa421582018-09-13 10:05:57 -0700225 startInt();
Yi Tseng930b0cd2020-10-04 22:33:22 -0700226 log.info("Started");
Carmelo Casconefa421582018-09-13 10:05:57 -0700227 }
228
229 @Deactivate
230 public void deactivate() {
231 deviceService.removeListener(deviceListener);
232 hostService.removeListener(hostListener);
233 intentIds = null;
234 intConfig.removeListener(intConfigListener);
235 intConfig = null;
236 intStarted.removeListener(intStartedListener);
237 intStarted = null;
238 intentMap.removeListener(intentMapListener);
239 intentMap = null;
240 devicesToConfigure.removeListener(devicesToConfigureListener);
241 devicesToConfigure.destroy();
242 devicesToConfigure = null;
243 // Cancel tasks (if any).
244 scheduledDeviceTasks.values().forEach(f -> {
245 f.cancel(true);
246 if (!f.isDone()) {
247 try {
248 f.get(1, TimeUnit.SECONDS);
249 } catch (InterruptedException | ExecutionException | TimeoutException e) {
250 // Don't care, we are terminating the service anyways.
251 }
252 }
253 });
254 // Clean up INT rules from existing devices.
255 deviceService.getDevices().forEach(d -> cleanupDevice(d.id()));
Yi Tseng930b0cd2020-10-04 22:33:22 -0700256 netcfgService.removeListener(appConfigListener);
257 netcfgRegistry.unregisterConfigFactory(intAppConfigFactory);
Carmelo Casconefa421582018-09-13 10:05:57 -0700258 log.info("Deactivated");
259 }
260
261 @Override
262 public void startInt() {
263 // Atomic value event will trigger device configure.
264 intStarted.set(true);
265 }
266
267 @Override
268 public void startInt(Set<DeviceId> deviceIds) {
269 log.warn("Starting INT for a subset of devices is not supported");
270 }
271
272 @Override
273 public void stopInt() {
274 // Atomic value event will trigger device configure.
275 intStarted.set(false);
276 }
277
278 @Override
279 public void stopInt(Set<DeviceId> deviceIds) {
280 log.warn("Stopping INT for a subset of devices is not supported");
281 }
282
283 @Override
Carmelo Casconedefc74e2020-07-17 15:27:02 -0700284 public void setConfig(IntDeviceConfig cfg) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700285 checkNotNull(cfg);
286 // Atomic value event will trigger device configure.
287 intConfig.set(cfg);
288 }
289
290 @Override
Carmelo Casconedefc74e2020-07-17 15:27:02 -0700291 public IntDeviceConfig getConfig() {
Carmelo Casconefa421582018-09-13 10:05:57 -0700292 return intConfig.get();
293 }
294
295 @Override
296 public IntIntentId installIntIntent(IntIntent intent) {
297 checkNotNull(intent);
298 final Integer intentId = (int) intentIds.nextId();
299 final IntIntentId intIntentId = IntIntentId.valueOf(intentId);
300 // Intent map event will trigger device configure.
301 intentMap.put(intIntentId, intent);
302 return intIntentId;
303 }
304
305 @Override
306 public void removeIntIntent(IntIntentId intentId) {
307 checkNotNull(intentId);
308 // Intent map event will trigger device configure.
Yi Tseng930b0cd2020-10-04 22:33:22 -0700309 if (!intentMap.containsKey(intentId)) {
310 log.warn("INT intent {} does not exists, skip removing the intent.", intentId);
311 return;
312 }
313 intentMap.remove(intentId);
Carmelo Casconefa421582018-09-13 10:05:57 -0700314 }
315
316 @Override
317 public IntIntent getIntIntent(IntIntentId intentId) {
318 return Optional.ofNullable(intentMap.get(intentId).value()).orElse(null);
319 }
320
321 @Override
322 public Map<IntIntentId, IntIntent> getIntIntents() {
323 return intentMap.asJavaMap();
324 }
325
326 private boolean isConfigTaskValid(DeviceId deviceId, long creationTime) {
327 Versioned<?> versioned = devicesToConfigure.get(deviceId);
328 return versioned != null && versioned.creationTime() == creationTime;
329 }
330
331 private boolean isIntStarted() {
332 return intStarted.get();
333 }
334
335 private boolean isNotIntConfigured() {
336 return intConfig.get() == null;
337 }
338
339 private boolean isIntProgrammable(DeviceId deviceId) {
340 final Device device = deviceService.getDevice(deviceId);
341 return device != null && device.is(IntProgrammable.class);
342 }
343
344 private void triggerDeviceConfigure(DeviceId deviceId) {
345 if (isIntProgrammable(deviceId)) {
346 devicesToConfigure.put(deviceId, System.nanoTime());
347 }
348 }
349
350 private void triggerAllDeviceConfigure() {
351 deviceService.getDevices().forEach(d -> triggerDeviceConfigure(d.id()));
352 }
353
354 private void configDeviceTask(DeviceId deviceId, long creationTime) {
355 if (isConfigTaskValid(deviceId, creationTime)) {
356 // Task outdated.
357 return;
358 }
359 if (!deviceService.isAvailable(deviceId)) {
360 return;
361 }
362 final MastershipRole role = mastershipService.requestRoleForSync(deviceId);
363 if (!role.equals(MastershipRole.MASTER)) {
364 return;
365 }
366 deviceLocks.get(deviceId).lock();
367 try {
368 // Clean up first.
369 cleanupDevice(deviceId);
370 if (!configDevice(deviceId)) {
371 // Clean up if fails.
372 cleanupDevice(deviceId);
373 return;
374 }
375 devicesToConfigure.remove(deviceId);
376 } finally {
377 deviceLocks.get(deviceId).unlock();
378 }
379 }
380
381 private void cleanupDevice(DeviceId deviceId) {
382 final Device device = deviceService.getDevice(deviceId);
383 if (device == null || !device.is(IntProgrammable.class)) {
384 return;
385 }
386 device.as(IntProgrammable.class).cleanup();
387 }
388
389 private boolean configDevice(DeviceId deviceId) {
390 // Returns true if config was successful, false if not and a clean up is
391 // needed.
392 final Device device = deviceService.getDevice(deviceId);
393 if (device == null || !device.is(IntProgrammable.class)) {
394 return true;
395 }
396
397 if (isNotIntConfigured()) {
398 log.warn("Missing INT config, aborting programming of INT device {}", deviceId);
399 return true;
400 }
401
402 final boolean isEdge = !hostService.getConnectedHosts(deviceId).isEmpty();
403 final IntDeviceRole intDeviceRole = isEdge
404 ? IntDeviceRole.SOURCE_SINK
405 : IntDeviceRole.TRANSIT;
406
407 log.info("Started programming of INT device {} with role {}...",
408 deviceId, intDeviceRole);
409
410 final IntProgrammable intProg = device.as(IntProgrammable.class);
411
412 if (!isIntStarted()) {
413 // Leave device with no INT configuration.
414 return true;
415 }
416
417 if (!intProg.init()) {
418 log.warn("Unable to init INT pipeline on {}", deviceId);
419 return false;
420 }
421
422 if (intDeviceRole != IntDeviceRole.SOURCE_SINK) {
423 // Stop here, no more configuration needed for transit devices.
424 return true;
425 }
426
427 if (intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SINK)) {
428 if (!intProg.setupIntConfig(intConfig.get())) {
429 log.warn("Unable to apply INT report config on {}", deviceId);
430 return false;
431 }
432 }
433
434 // Port configuration.
435 final Set<PortNumber> hostPorts = deviceService.getPorts(deviceId)
436 .stream()
437 .map(port -> new ConnectPoint(deviceId, port.number()))
438 .filter(cp -> !hostService.getConnectedHosts(cp).isEmpty())
439 .map(ConnectPoint::port)
440 .collect(Collectors.toSet());
441
442 for (PortNumber port : hostPorts) {
443 if (intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SOURCE)) {
444 log.info("Setting port {}/{} as INT source port...", deviceId, port);
445 if (!intProg.setSourcePort(port)) {
446 log.warn("Unable to set INT source port {} on {}", port, deviceId);
447 return false;
448 }
449 }
450 if (intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SINK)) {
451 log.info("Setting port {}/{} as INT sink port...", deviceId, port);
452 if (!intProg.setSinkPort(port)) {
453 log.warn("Unable to set INT sink port {} on {}", port, deviceId);
454 return false;
455 }
456 }
457 }
458
459 if (!intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SOURCE)) {
460 // Stop here, no more configuration needed for sink devices.
461 return true;
462 }
463
464 // Apply intents.
465 // This is a trivial implementation where we simply get the
466 // corresponding INT objective from an intent and we apply to all source
467 // device.
468 final Collection<IntObjective> objectives = intentMap.values().stream()
469 .map(v -> getIntObjective(v.value()))
470 .collect(Collectors.toList());
471 int appliedCount = 0;
472 for (IntObjective objective : objectives) {
473 if (intProg.addIntObjective(objective)) {
474 appliedCount = appliedCount + 1;
475 }
476 }
477
478 log.info("Completed programming of {}, applied {} INT objectives of {} total",
479 deviceId, appliedCount, objectives.size());
480
481 return true;
482 }
483
484 private IntObjective getIntObjective(IntIntent intent) {
Carmelo Casconedefc74e2020-07-17 15:27:02 -0700485 // FIXME: we are ignore intent.headerType()
486 // what should we do with it?
Carmelo Casconefa421582018-09-13 10:05:57 -0700487 return new IntObjective.Builder()
488 .withSelector(intent.selector())
489 .withMetadataTypes(intent.metadataTypes())
Carmelo Casconefa421582018-09-13 10:05:57 -0700490 .build();
491 }
492
493 /* Event listeners which trigger device configuration. */
494
495 private class InternalHostListener implements HostListener {
496 @Override
497 public void event(HostEvent event) {
498 final DeviceId deviceId = event.subject().location().deviceId();
499 triggerDeviceConfigure(deviceId);
500 }
501 }
502
503 private class InternalDeviceListener implements DeviceListener {
504 @Override
505 public void event(DeviceEvent event) {
506 switch (event.type()) {
507 case DEVICE_ADDED:
508 case DEVICE_UPDATED:
509 case DEVICE_REMOVED:
510 case DEVICE_SUSPENDED:
511 case DEVICE_AVAILABILITY_CHANGED:
512 case PORT_ADDED:
513 case PORT_UPDATED:
514 case PORT_REMOVED:
515 triggerDeviceConfigure(event.subject().id());
516 return;
517 case PORT_STATS_UPDATED:
518 return;
519 default:
520 log.warn("Unknown device event type {}", event.type());
521 }
522 }
523 }
524
525 private class InternalIntentMapListener
526 implements MapEventListener<IntIntentId, IntIntent> {
527 @Override
528 public void event(MapEvent<IntIntentId, IntIntent> event) {
529 triggerAllDeviceConfigure();
530 }
531 }
532
533 private class InternalIntConfigListener
Carmelo Casconedefc74e2020-07-17 15:27:02 -0700534 implements AtomicValueEventListener<IntDeviceConfig> {
Carmelo Casconefa421582018-09-13 10:05:57 -0700535 @Override
Carmelo Casconedefc74e2020-07-17 15:27:02 -0700536 public void event(AtomicValueEvent<IntDeviceConfig> event) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700537 triggerAllDeviceConfigure();
538 }
539 }
540
541 private class InternalIntStartedListener
542 implements AtomicValueEventListener<Boolean> {
543 @Override
544 public void event(AtomicValueEvent<Boolean> event) {
545 triggerAllDeviceConfigure();
546 }
547 }
548
549 private class InternalDeviceToConfigureListener
550 implements MapEventListener<DeviceId, Long> {
551 @Override
552 public void event(MapEvent<DeviceId, Long> event) {
553 if (event.type().equals(MapEvent.Type.REMOVE) ||
554 event.newValue() == null) {
555 return;
556 }
557 // Schedule task in the future. Wait for events for this device to
558 // stabilize.
559 final DeviceId deviceId = event.key();
560 final long creationTime = event.newValue().creationTime();
561 ScheduledFuture<?> newTask = SharedScheduledExecutors.newTimeout(
562 () -> configDeviceTask(deviceId, creationTime),
563 CONFIG_EVENT_DELAY, TimeUnit.SECONDS);
564 ScheduledFuture<?> oldTask = scheduledDeviceTasks.put(deviceId, newTask);
565 if (oldTask != null) {
566 oldTask.cancel(false);
567 }
568 }
569 }
Yi Tseng0f1ffd12020-09-18 11:10:47 -0700570
571 private class IntAppConfigListener implements NetworkConfigListener {
572
573 @Override
574 public void event(NetworkConfigEvent event) {
575 switch (event.type()) {
576 case CONFIG_ADDED:
577 case CONFIG_UPDATED:
578 event.config()
579 .map(config -> (IntReportConfig) config)
580 .ifPresent(config -> {
581 IntDeviceConfig intDeviceConfig = IntDeviceConfig.builder()
582 .withMinFlowHopLatencyChangeNs(config.minFlowHopLatencyChangeNs())
583 .withCollectorPort(config.collectorPort())
584 .withCollectorIp(config.collectorIp())
585 .enabled(true)
586 .build();
587 setConfig(intDeviceConfig);
588 });
589 break;
590 // TODO: Support removing INT config.
591 default:
592 break;
593 }
594 }
595
596 @Override
597 public boolean isRelevant(NetworkConfigEvent event) {
598 return event.configClass() == IntReportConfig.class;
599 }
600 }
Carmelo Casconefa421582018-09-13 10:05:57 -0700601}