blob: bf7dc184bca82438e68ca9772a0f84d7268d5783 [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 Tsengda707962020-09-18 11:10:47 -070024import org.onosproject.net.behaviour.inbandtelemetry.IntReportConfig;
Carmelo Casconeb330fc72020-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 Casconeb330fc72020-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 Tsengda707962020-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;
Yi Tseng906febe2021-03-29 01:58:15 -070047import org.onosproject.net.flow.DefaultTrafficSelector;
48import org.onosproject.net.flow.TrafficSelector;
Carmelo Casconefa421582018-09-13 10:05:57 -070049import org.onosproject.net.host.HostEvent;
50import org.onosproject.net.host.HostListener;
51import org.onosproject.net.host.HostService;
52import org.onosproject.store.serializers.KryoNamespaces;
53import org.onosproject.store.service.AtomicIdGenerator;
54import org.onosproject.store.service.AtomicValue;
55import org.onosproject.store.service.AtomicValueEvent;
56import org.onosproject.store.service.AtomicValueEventListener;
57import org.onosproject.store.service.ConsistentMap;
58import org.onosproject.store.service.MapEvent;
59import org.onosproject.store.service.MapEventListener;
60import org.onosproject.store.service.Serializer;
61import org.onosproject.store.service.StorageService;
62import org.onosproject.store.service.Versioned;
Ray Milkeydb57f1c2018-10-09 10:39:29 -070063import org.osgi.service.component.annotations.Activate;
64import org.osgi.service.component.annotations.Component;
65import org.osgi.service.component.annotations.Deactivate;
66import org.osgi.service.component.annotations.Reference;
67import org.osgi.service.component.annotations.ReferenceCardinality;
Carmelo Casconefa421582018-09-13 10:05:57 -070068import org.slf4j.Logger;
69
Carmelo Casconefa421582018-09-13 10:05:57 -070070import java.util.Map;
71import java.util.Optional;
72import java.util.Set;
73import java.util.concurrent.ConcurrentMap;
74import java.util.concurrent.ExecutionException;
pierventrec4ab3552021-07-09 22:42:17 +020075import java.util.concurrent.ExecutorService;
Carmelo Casconefa421582018-09-13 10:05:57 -070076import java.util.concurrent.ScheduledFuture;
77import java.util.concurrent.TimeUnit;
78import java.util.concurrent.TimeoutException;
79import java.util.concurrent.locks.Lock;
80import java.util.stream.Collectors;
81
82import static com.google.common.base.Preconditions.checkNotNull;
pierventrec4ab3552021-07-09 22:42:17 +020083import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
84import static org.onlab.util.Tools.groupedThreads;
Carmelo Casconefa421582018-09-13 10:05:57 -070085import static org.slf4j.LoggerFactory.getLogger;
86
87/**
88 * Simple implementation of IntService, for controlling INT-capable pipelines.
89 * <p>
90 * All INT intents are converted to an equivalent INT objective and applied to
91 * all SOURCE_SINK devices. A device is deemed SOURCE_SINK if it has at least
92 * one host attached.
93 * <p>
94 * The implementation listens for different types of events and when required it
95 * configures a device by cleaning-up any previous state and applying the new
96 * one.
97 */
Ray Milkeydb57f1c2018-10-09 10:39:29 -070098@Component(immediate = true, service = IntService.class)
Carmelo Casconefa421582018-09-13 10:05:57 -070099public class SimpleIntManager implements IntService {
100
101 private final Logger log = getLogger(getClass());
102
103 private static final int CONFIG_EVENT_DELAY = 5; // Seconds.
104
105 private static final String APP_NAME = "org.onosproject.inbandtelemetry";
106
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700108 protected CoreService coreService;
Carmelo Casconefa421582018-09-13 10:05:57 -0700109
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700111 protected DeviceService deviceService;
Carmelo Casconefa421582018-09-13 10:05:57 -0700112
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700114 protected StorageService storageService;
Carmelo Casconefa421582018-09-13 10:05:57 -0700115
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700117 protected MastershipService mastershipService;
Carmelo Casconefa421582018-09-13 10:05:57 -0700118
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700120 protected HostService hostService;
Carmelo Casconefa421582018-09-13 10:05:57 -0700121
Yi Tsengda707962020-09-18 11:10:47 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700123 protected NetworkConfigService netcfgService;
Yi Tsengda707962020-09-18 11:10:47 -0700124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700126 protected NetworkConfigRegistry netcfgRegistry;
Yi Tsengda707962020-09-18 11:10:47 -0700127
Carmelo Casconefa421582018-09-13 10:05:57 -0700128 private final Striped<Lock> deviceLocks = Striped.lock(10);
129
130 private final ConcurrentMap<DeviceId, ScheduledFuture<?>> scheduledDeviceTasks = Maps.newConcurrentMap();
131
132 // Distributed state.
133 private ConsistentMap<IntIntentId, IntIntent> intentMap;
134 private ConsistentMap<DeviceId, Long> devicesToConfigure;
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700135 private AtomicValue<IntDeviceConfig> intConfig;
Carmelo Casconefa421582018-09-13 10:05:57 -0700136 private AtomicValue<Boolean> intStarted;
137 private AtomicIdGenerator intentIds;
138
139 // Event listeners.
140 private final InternalHostListener hostListener = new InternalHostListener();
141 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
142 private final InternalIntentMapListener intentMapListener = new InternalIntentMapListener();
143 private final InternalIntConfigListener intConfigListener = new InternalIntConfigListener();
144 private final InternalIntStartedListener intStartedListener = new InternalIntStartedListener();
145 private final InternalDeviceToConfigureListener devicesToConfigureListener =
146 new InternalDeviceToConfigureListener();
Yi Tsengda707962020-09-18 11:10:47 -0700147 private final NetworkConfigListener appConfigListener = new IntAppConfigListener();
148
149 private final ConfigFactory<ApplicationId, IntReportConfig> intAppConfigFactory =
150 new ConfigFactory<>(SubjectFactories.APP_SUBJECT_FACTORY,
151 IntReportConfig.class, "report") {
152 @Override
153 public IntReportConfig createConfig() {
154 return new IntReportConfig();
155 }
156 };
Carmelo Casconefa421582018-09-13 10:05:57 -0700157
pierventrec4ab3552021-07-09 22:42:17 +0200158 protected ExecutorService eventExecutor;
159
Carmelo Casconefa421582018-09-13 10:05:57 -0700160 @Activate
161 public void activate() {
162
163 final ApplicationId appId = coreService.registerApplication(APP_NAME);
164
165 KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
166 .register(KryoNamespaces.API)
167 .register(IntIntent.class)
168 .register(IntIntentId.class)
169 .register(IntDeviceRole.class)
170 .register(IntIntent.IntHeaderType.class)
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700171 .register(IntMetadataType.class)
Carmelo Casconefa421582018-09-13 10:05:57 -0700172 .register(IntIntent.IntReportType.class)
173 .register(IntIntent.TelemetryMode.class)
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700174 .register(IntDeviceConfig.class)
175 .register(IntDeviceConfig.TelemetrySpec.class);
Carmelo Casconefa421582018-09-13 10:05:57 -0700176
177 devicesToConfigure = storageService.<DeviceId, Long>consistentMapBuilder()
178 .withSerializer(Serializer.using(serializer.build()))
179 .withName("onos-int-devices-to-configure")
180 .withApplicationId(appId)
181 .withPurgeOnUninstall()
182 .build();
183 devicesToConfigure.addListener(devicesToConfigureListener);
184
185 intentMap = storageService.<IntIntentId, IntIntent>consistentMapBuilder()
186 .withSerializer(Serializer.using(serializer.build()))
187 .withName("onos-int-intents")
188 .withApplicationId(appId)
189 .withPurgeOnUninstall()
190 .build();
191 intentMap.addListener(intentMapListener);
192
193 intStarted = storageService.<Boolean>atomicValueBuilder()
194 .withSerializer(Serializer.using(serializer.build()))
195 .withName("onos-int-started")
196 .withApplicationId(appId)
197 .build()
198 .asAtomicValue();
199 intStarted.addListener(intStartedListener);
200
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700201 intConfig = storageService.<IntDeviceConfig>atomicValueBuilder()
Carmelo Casconefa421582018-09-13 10:05:57 -0700202 .withSerializer(Serializer.using(serializer.build()))
203 .withName("onos-int-config")
204 .withApplicationId(appId)
205 .build()
206 .asAtomicValue();
207 intConfig.addListener(intConfigListener);
208
209 intentIds = storageService.getAtomicIdGenerator("int-intent-id-generator");
210
211 // Bootstrap config for already existing devices.
212 triggerAllDeviceConfigure();
213
pierventrec4ab3552021-07-09 22:42:17 +0200214 // Bootstrap core event executor before adding listener
215 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads(
216 "onos/int", "events-%d", log));
217
Carmelo Casconefa421582018-09-13 10:05:57 -0700218 hostService.addListener(hostListener);
219 deviceService.addListener(deviceListener);
220
Yi Tsengda707962020-09-18 11:10:47 -0700221 netcfgRegistry.registerConfigFactory(intAppConfigFactory);
222 netcfgService.addListener(appConfigListener);
Yi Tsengc4f90a52020-10-04 22:33:22 -0700223 // Initialize the INT report
224 IntReportConfig reportConfig = netcfgService.getConfig(appId, IntReportConfig.class);
225 if (reportConfig != null) {
226 IntDeviceConfig intDeviceConfig = IntDeviceConfig.builder()
227 .withMinFlowHopLatencyChangeNs(reportConfig.minFlowHopLatencyChangeNs())
228 .withCollectorPort(reportConfig.collectorPort())
229 .withCollectorIp(reportConfig.collectorIp())
230 .enabled(true)
231 .build();
232 setConfig(intDeviceConfig);
233 }
Yi Tsengda707962020-09-18 11:10:47 -0700234
Carmelo Casconefa421582018-09-13 10:05:57 -0700235 startInt();
Yi Tsengc4f90a52020-10-04 22:33:22 -0700236 log.info("Started");
Carmelo Casconefa421582018-09-13 10:05:57 -0700237 }
238
239 @Deactivate
240 public void deactivate() {
241 deviceService.removeListener(deviceListener);
242 hostService.removeListener(hostListener);
243 intentIds = null;
244 intConfig.removeListener(intConfigListener);
245 intConfig = null;
246 intStarted.removeListener(intStartedListener);
247 intStarted = null;
248 intentMap.removeListener(intentMapListener);
249 intentMap = null;
250 devicesToConfigure.removeListener(devicesToConfigureListener);
251 devicesToConfigure.destroy();
252 devicesToConfigure = null;
253 // Cancel tasks (if any).
254 scheduledDeviceTasks.values().forEach(f -> {
255 f.cancel(true);
256 if (!f.isDone()) {
257 try {
258 f.get(1, TimeUnit.SECONDS);
259 } catch (InterruptedException | ExecutionException | TimeoutException e) {
260 // Don't care, we are terminating the service anyways.
261 }
262 }
263 });
264 // Clean up INT rules from existing devices.
265 deviceService.getDevices().forEach(d -> cleanupDevice(d.id()));
Yi Tsengc4f90a52020-10-04 22:33:22 -0700266 netcfgService.removeListener(appConfigListener);
267 netcfgRegistry.unregisterConfigFactory(intAppConfigFactory);
pierventrec4ab3552021-07-09 22:42:17 +0200268 eventExecutor.shutdownNow();
269 eventExecutor = null;
Carmelo Casconefa421582018-09-13 10:05:57 -0700270 log.info("Deactivated");
271 }
272
273 @Override
274 public void startInt() {
275 // Atomic value event will trigger device configure.
276 intStarted.set(true);
277 }
278
279 @Override
280 public void startInt(Set<DeviceId> deviceIds) {
281 log.warn("Starting INT for a subset of devices is not supported");
282 }
283
284 @Override
285 public void stopInt() {
286 // Atomic value event will trigger device configure.
287 intStarted.set(false);
288 }
289
290 @Override
291 public void stopInt(Set<DeviceId> deviceIds) {
292 log.warn("Stopping INT for a subset of devices is not supported");
293 }
294
295 @Override
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700296 public void setConfig(IntDeviceConfig cfg) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700297 checkNotNull(cfg);
298 // Atomic value event will trigger device configure.
299 intConfig.set(cfg);
300 }
301
302 @Override
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700303 public IntDeviceConfig getConfig() {
Carmelo Casconefa421582018-09-13 10:05:57 -0700304 return intConfig.get();
305 }
306
307 @Override
308 public IntIntentId installIntIntent(IntIntent intent) {
309 checkNotNull(intent);
310 final Integer intentId = (int) intentIds.nextId();
311 final IntIntentId intIntentId = IntIntentId.valueOf(intentId);
312 // Intent map event will trigger device configure.
313 intentMap.put(intIntentId, intent);
314 return intIntentId;
315 }
316
317 @Override
318 public void removeIntIntent(IntIntentId intentId) {
319 checkNotNull(intentId);
320 // Intent map event will trigger device configure.
Yi Tsengc4f90a52020-10-04 22:33:22 -0700321 if (!intentMap.containsKey(intentId)) {
322 log.warn("INT intent {} does not exists, skip removing the intent.", intentId);
323 return;
324 }
325 intentMap.remove(intentId);
Carmelo Casconefa421582018-09-13 10:05:57 -0700326 }
327
328 @Override
329 public IntIntent getIntIntent(IntIntentId intentId) {
330 return Optional.ofNullable(intentMap.get(intentId).value()).orElse(null);
331 }
332
333 @Override
334 public Map<IntIntentId, IntIntent> getIntIntents() {
335 return intentMap.asJavaMap();
336 }
337
338 private boolean isConfigTaskValid(DeviceId deviceId, long creationTime) {
339 Versioned<?> versioned = devicesToConfigure.get(deviceId);
340 return versioned != null && versioned.creationTime() == creationTime;
341 }
342
343 private boolean isIntStarted() {
344 return intStarted.get();
345 }
346
347 private boolean isNotIntConfigured() {
348 return intConfig.get() == null;
349 }
350
351 private boolean isIntProgrammable(DeviceId deviceId) {
352 final Device device = deviceService.getDevice(deviceId);
353 return device != null && device.is(IntProgrammable.class);
354 }
355
356 private void triggerDeviceConfigure(DeviceId deviceId) {
357 if (isIntProgrammable(deviceId)) {
358 devicesToConfigure.put(deviceId, System.nanoTime());
359 }
360 }
361
362 private void triggerAllDeviceConfigure() {
363 deviceService.getDevices().forEach(d -> triggerDeviceConfigure(d.id()));
364 }
365
366 private void configDeviceTask(DeviceId deviceId, long creationTime) {
367 if (isConfigTaskValid(deviceId, creationTime)) {
368 // Task outdated.
369 return;
370 }
371 if (!deviceService.isAvailable(deviceId)) {
372 return;
373 }
374 final MastershipRole role = mastershipService.requestRoleForSync(deviceId);
375 if (!role.equals(MastershipRole.MASTER)) {
376 return;
377 }
378 deviceLocks.get(deviceId).lock();
379 try {
380 // Clean up first.
381 cleanupDevice(deviceId);
382 if (!configDevice(deviceId)) {
383 // Clean up if fails.
384 cleanupDevice(deviceId);
385 return;
386 }
387 devicesToConfigure.remove(deviceId);
388 } finally {
389 deviceLocks.get(deviceId).unlock();
390 }
391 }
392
393 private void cleanupDevice(DeviceId deviceId) {
394 final Device device = deviceService.getDevice(deviceId);
395 if (device == null || !device.is(IntProgrammable.class)) {
396 return;
397 }
398 device.as(IntProgrammable.class).cleanup();
399 }
400
Yi Tsengd14f1a32020-10-13 19:15:12 -0700401 protected boolean configDevice(DeviceId deviceId) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700402 // Returns true if config was successful, false if not and a clean up is
403 // needed.
404 final Device device = deviceService.getDevice(deviceId);
405 if (device == null || !device.is(IntProgrammable.class)) {
406 return true;
407 }
408
409 if (isNotIntConfigured()) {
410 log.warn("Missing INT config, aborting programming of INT device {}", deviceId);
411 return true;
412 }
413
414 final boolean isEdge = !hostService.getConnectedHosts(deviceId).isEmpty();
Yi Tsengd14f1a32020-10-13 19:15:12 -0700415 final IntDeviceRole intDeviceRole =
416 isEdge ? IntDeviceRole.SOURCE_SINK : IntDeviceRole.TRANSIT;
Carmelo Casconefa421582018-09-13 10:05:57 -0700417
418 log.info("Started programming of INT device {} with role {}...",
Yi Tsengd14f1a32020-10-13 19:15:12 -0700419 deviceId, intDeviceRole);
Carmelo Casconefa421582018-09-13 10:05:57 -0700420
421 final IntProgrammable intProg = device.as(IntProgrammable.class);
422
423 if (!isIntStarted()) {
424 // Leave device with no INT configuration.
425 return true;
426 }
427
428 if (!intProg.init()) {
429 log.warn("Unable to init INT pipeline on {}", deviceId);
430 return false;
431 }
432
Yi Tsengd14f1a32020-10-13 19:15:12 -0700433 boolean supportSource = intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SOURCE);
434 boolean supportSink = intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SINK);
435 boolean supportPostcard = intProg.supportsFunctionality(IntProgrammable.IntFunctionality.POSTCARD);
436
437 if (intDeviceRole != IntDeviceRole.SOURCE_SINK && !supportPostcard) {
438 // Stop here, no more configuration needed for transit devices unless it support postcard.
Carmelo Casconefa421582018-09-13 10:05:57 -0700439 return true;
440 }
441
Yi Tsengd14f1a32020-10-13 19:15:12 -0700442 if (supportSink || supportPostcard) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700443 if (!intProg.setupIntConfig(intConfig.get())) {
444 log.warn("Unable to apply INT report config on {}", deviceId);
445 return false;
446 }
447 }
448
449 // Port configuration.
450 final Set<PortNumber> hostPorts = deviceService.getPorts(deviceId)
451 .stream()
452 .map(port -> new ConnectPoint(deviceId, port.number()))
453 .filter(cp -> !hostService.getConnectedHosts(cp).isEmpty())
454 .map(ConnectPoint::port)
455 .collect(Collectors.toSet());
456
457 for (PortNumber port : hostPorts) {
Yi Tsengd14f1a32020-10-13 19:15:12 -0700458 if (supportSource) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700459 log.info("Setting port {}/{} as INT source port...", deviceId, port);
460 if (!intProg.setSourcePort(port)) {
461 log.warn("Unable to set INT source port {} on {}", port, deviceId);
462 return false;
463 }
464 }
Yi Tsengd14f1a32020-10-13 19:15:12 -0700465 if (supportSink) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700466 log.info("Setting port {}/{} as INT sink port...", deviceId, port);
467 if (!intProg.setSinkPort(port)) {
468 log.warn("Unable to set INT sink port {} on {}", port, deviceId);
469 return false;
470 }
471 }
472 }
473
Yi Tsengd14f1a32020-10-13 19:15:12 -0700474 if (!supportSource && !supportPostcard) {
475 // Stop here, no more configuration needed for sink devices unless
476 // it supports postcard mode.
Carmelo Casconefa421582018-09-13 10:05:57 -0700477 return true;
478 }
479
480 // Apply intents.
481 // This is a trivial implementation where we simply get the
Yi Tsengd14f1a32020-10-13 19:15:12 -0700482 // corresponding INT objective from an intent and we apply to all
483 // device which support reporting.
Carmelo Casconefa421582018-09-13 10:05:57 -0700484 int appliedCount = 0;
Yi Tsengd14f1a32020-10-13 19:15:12 -0700485 for (Versioned<IntIntent> versionedIntent : intentMap.values()) {
486 IntIntent intent = versionedIntent.value();
487 IntObjective intObjective = getIntObjective(intent);
488 if (intent.telemetryMode() == IntIntent.TelemetryMode.INBAND_TELEMETRY && supportSource) {
489 intProg.addIntObjective(intObjective);
490 appliedCount++;
491 } else if (intent.telemetryMode() == IntIntent.TelemetryMode.POSTCARD && supportPostcard) {
492 intProg.addIntObjective(intObjective);
493 appliedCount++;
494 } else {
495 log.warn("Device {} does not support intent {}.", deviceId, intent);
Carmelo Casconefa421582018-09-13 10:05:57 -0700496 }
497 }
Carmelo Casconefa421582018-09-13 10:05:57 -0700498 log.info("Completed programming of {}, applied {} INT objectives of {} total",
Yi Tsengd14f1a32020-10-13 19:15:12 -0700499 deviceId, appliedCount, intentMap.size());
Carmelo Casconefa421582018-09-13 10:05:57 -0700500 return true;
501 }
502
503 private IntObjective getIntObjective(IntIntent intent) {
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700504 // FIXME: we are ignore intent.headerType()
505 // what should we do with it?
Carmelo Casconefa421582018-09-13 10:05:57 -0700506 return new IntObjective.Builder()
507 .withSelector(intent.selector())
508 .withMetadataTypes(intent.metadataTypes())
Carmelo Casconefa421582018-09-13 10:05:57 -0700509 .build();
510 }
511
512 /* Event listeners which trigger device configuration. */
513
514 private class InternalHostListener implements HostListener {
515 @Override
516 public void event(HostEvent event) {
pierventrec4ab3552021-07-09 22:42:17 +0200517 eventExecutor.execute(() -> {
518 final DeviceId deviceId = event.subject().location().deviceId();
519 triggerDeviceConfigure(deviceId);
520 });
Carmelo Casconefa421582018-09-13 10:05:57 -0700521 }
522 }
523
524 private class InternalDeviceListener implements DeviceListener {
525 @Override
526 public void event(DeviceEvent event) {
pierventrec4ab3552021-07-09 22:42:17 +0200527 eventExecutor.execute(() -> {
528 switch (event.type()) {
529 case DEVICE_ADDED:
530 case DEVICE_UPDATED:
531 case DEVICE_REMOVED:
532 case DEVICE_SUSPENDED:
533 case DEVICE_AVAILABILITY_CHANGED:
534 case PORT_ADDED:
535 case PORT_UPDATED:
536 case PORT_REMOVED:
537 triggerDeviceConfigure(event.subject().id());
538 return;
539 case PORT_STATS_UPDATED:
540 return;
541 default:
542 log.warn("Unknown device event type {}", event.type());
543 }
544 });
Carmelo Casconefa421582018-09-13 10:05:57 -0700545 }
546 }
547
548 private class InternalIntentMapListener
549 implements MapEventListener<IntIntentId, IntIntent> {
550 @Override
551 public void event(MapEvent<IntIntentId, IntIntent> event) {
552 triggerAllDeviceConfigure();
553 }
554 }
555
556 private class InternalIntConfigListener
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700557 implements AtomicValueEventListener<IntDeviceConfig> {
Carmelo Casconefa421582018-09-13 10:05:57 -0700558 @Override
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700559 public void event(AtomicValueEvent<IntDeviceConfig> event) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700560 triggerAllDeviceConfigure();
561 }
562 }
563
564 private class InternalIntStartedListener
565 implements AtomicValueEventListener<Boolean> {
566 @Override
567 public void event(AtomicValueEvent<Boolean> event) {
568 triggerAllDeviceConfigure();
569 }
570 }
571
572 private class InternalDeviceToConfigureListener
573 implements MapEventListener<DeviceId, Long> {
574 @Override
575 public void event(MapEvent<DeviceId, Long> event) {
576 if (event.type().equals(MapEvent.Type.REMOVE) ||
577 event.newValue() == null) {
578 return;
579 }
580 // Schedule task in the future. Wait for events for this device to
581 // stabilize.
582 final DeviceId deviceId = event.key();
583 final long creationTime = event.newValue().creationTime();
584 ScheduledFuture<?> newTask = SharedScheduledExecutors.newTimeout(
585 () -> configDeviceTask(deviceId, creationTime),
586 CONFIG_EVENT_DELAY, TimeUnit.SECONDS);
587 ScheduledFuture<?> oldTask = scheduledDeviceTasks.put(deviceId, newTask);
588 if (oldTask != null) {
589 oldTask.cancel(false);
590 }
591 }
592 }
Yi Tsengda707962020-09-18 11:10:47 -0700593
594 private class IntAppConfigListener implements NetworkConfigListener {
595
596 @Override
597 public void event(NetworkConfigEvent event) {
pierventrec4ab3552021-07-09 22:42:17 +0200598 eventExecutor.execute(() -> {
599 if (event.configClass() == IntReportConfig.class) {
600 switch (event.type()) {
601 case CONFIG_ADDED:
602 case CONFIG_UPDATED:
603 event.config()
604 .map(config -> (IntReportConfig) config)
605 .ifPresent(config -> {
606 IntDeviceConfig intDeviceConfig = IntDeviceConfig.builder()
607 .withMinFlowHopLatencyChangeNs(config.minFlowHopLatencyChangeNs())
608 .withCollectorPort(config.collectorPort())
609 .withCollectorIp(config.collectorIp())
610 .enabled(true)
611 .build();
612 setConfig(intDeviceConfig);
Yi Tseng906febe2021-03-29 01:58:15 -0700613
pierventrec4ab3552021-07-09 22:42:17 +0200614 // For each watched subnet, we install two INT rules.
615 // One match on the source, another match on the destination.
616 intentMap.clear();
617 config.watchSubnets().forEach(subnet -> {
618 IntIntent.Builder intIntentBuilder = IntIntent.builder()
619 .withReportType(IntIntent.IntReportType.TRACKED_FLOW)
620 .withReportType(IntIntent.IntReportType.DROPPED_PACKET)
621 .withReportType(IntIntent.IntReportType.CONGESTED_QUEUE)
622 .withTelemetryMode(IntIntent.TelemetryMode.POSTCARD);
623 if (subnet.prefixLength() == 0) {
624 // Special case, match any packet
625 installIntIntent(intIntentBuilder
626 .withSelector(DefaultTrafficSelector.emptySelector())
627 .build());
628 } else {
629 TrafficSelector selector = DefaultTrafficSelector.builder()
630 .matchIPSrc(subnet)
631 .build();
632 installIntIntent(intIntentBuilder.withSelector(selector).build());
633 selector = DefaultTrafficSelector.builder()
634 .matchIPDst(subnet)
635 .build();
636 installIntIntent(intIntentBuilder.withSelector(selector).build());
637 }
638 });
639 });
640 break;
641 // TODO: Support removing INT config.
642 default:
643 break;
644 }
645 }
646 });
Yi Tsengda707962020-09-18 11:10:47 -0700647 }
648
Yi Tsengda707962020-09-18 11:10:47 -0700649 }
Carmelo Casconefa421582018-09-13 10:05:57 -0700650}