blob: ffb6b4f995a26f0dc245a15e9fb727b10ec75420 [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;
75import java.util.concurrent.ScheduledFuture;
76import java.util.concurrent.TimeUnit;
77import java.util.concurrent.TimeoutException;
78import java.util.concurrent.locks.Lock;
79import java.util.stream.Collectors;
80
81import static com.google.common.base.Preconditions.checkNotNull;
82import static org.slf4j.LoggerFactory.getLogger;
83
84/**
85 * Simple implementation of IntService, for controlling INT-capable pipelines.
86 * <p>
87 * All INT intents are converted to an equivalent INT objective and applied to
88 * all SOURCE_SINK devices. A device is deemed SOURCE_SINK if it has at least
89 * one host attached.
90 * <p>
91 * The implementation listens for different types of events and when required it
92 * configures a device by cleaning-up any previous state and applying the new
93 * one.
94 */
Ray Milkeydb57f1c2018-10-09 10:39:29 -070095@Component(immediate = true, service = IntService.class)
Carmelo Casconefa421582018-09-13 10:05:57 -070096public class SimpleIntManager implements IntService {
97
98 private final Logger log = getLogger(getClass());
99
100 private static final int CONFIG_EVENT_DELAY = 5; // Seconds.
101
102 private static final String APP_NAME = "org.onosproject.inbandtelemetry";
103
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700105 protected CoreService coreService;
Carmelo Casconefa421582018-09-13 10:05:57 -0700106
Ray Milkeydb57f1c2018-10-09 10:39:29 -0700107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700108 protected DeviceService deviceService;
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 StorageService storageService;
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 MastershipService mastershipService;
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 HostService hostService;
Carmelo Casconefa421582018-09-13 10:05:57 -0700118
Yi Tsengda707962020-09-18 11:10:47 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700120 protected NetworkConfigService netcfgService;
Yi Tsengda707962020-09-18 11:10:47 -0700121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengd14f1a32020-10-13 19:15:12 -0700123 protected NetworkConfigRegistry netcfgRegistry;
Yi Tsengda707962020-09-18 11:10:47 -0700124
Carmelo Casconefa421582018-09-13 10:05:57 -0700125 private final Striped<Lock> deviceLocks = Striped.lock(10);
126
127 private final ConcurrentMap<DeviceId, ScheduledFuture<?>> scheduledDeviceTasks = Maps.newConcurrentMap();
128
129 // Distributed state.
130 private ConsistentMap<IntIntentId, IntIntent> intentMap;
131 private ConsistentMap<DeviceId, Long> devicesToConfigure;
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700132 private AtomicValue<IntDeviceConfig> intConfig;
Carmelo Casconefa421582018-09-13 10:05:57 -0700133 private AtomicValue<Boolean> intStarted;
134 private AtomicIdGenerator intentIds;
135
136 // Event listeners.
137 private final InternalHostListener hostListener = new InternalHostListener();
138 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
139 private final InternalIntentMapListener intentMapListener = new InternalIntentMapListener();
140 private final InternalIntConfigListener intConfigListener = new InternalIntConfigListener();
141 private final InternalIntStartedListener intStartedListener = new InternalIntStartedListener();
142 private final InternalDeviceToConfigureListener devicesToConfigureListener =
143 new InternalDeviceToConfigureListener();
Yi Tsengda707962020-09-18 11:10:47 -0700144 private final NetworkConfigListener appConfigListener = new IntAppConfigListener();
145
146 private final ConfigFactory<ApplicationId, IntReportConfig> intAppConfigFactory =
147 new ConfigFactory<>(SubjectFactories.APP_SUBJECT_FACTORY,
148 IntReportConfig.class, "report") {
149 @Override
150 public IntReportConfig createConfig() {
151 return new IntReportConfig();
152 }
153 };
Carmelo Casconefa421582018-09-13 10:05:57 -0700154
155 @Activate
156 public void activate() {
157
158 final ApplicationId appId = coreService.registerApplication(APP_NAME);
159
160 KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
161 .register(KryoNamespaces.API)
162 .register(IntIntent.class)
163 .register(IntIntentId.class)
164 .register(IntDeviceRole.class)
165 .register(IntIntent.IntHeaderType.class)
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700166 .register(IntMetadataType.class)
Carmelo Casconefa421582018-09-13 10:05:57 -0700167 .register(IntIntent.IntReportType.class)
168 .register(IntIntent.TelemetryMode.class)
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700169 .register(IntDeviceConfig.class)
170 .register(IntDeviceConfig.TelemetrySpec.class);
Carmelo Casconefa421582018-09-13 10:05:57 -0700171
172 devicesToConfigure = storageService.<DeviceId, Long>consistentMapBuilder()
173 .withSerializer(Serializer.using(serializer.build()))
174 .withName("onos-int-devices-to-configure")
175 .withApplicationId(appId)
176 .withPurgeOnUninstall()
177 .build();
178 devicesToConfigure.addListener(devicesToConfigureListener);
179
180 intentMap = storageService.<IntIntentId, IntIntent>consistentMapBuilder()
181 .withSerializer(Serializer.using(serializer.build()))
182 .withName("onos-int-intents")
183 .withApplicationId(appId)
184 .withPurgeOnUninstall()
185 .build();
186 intentMap.addListener(intentMapListener);
187
188 intStarted = storageService.<Boolean>atomicValueBuilder()
189 .withSerializer(Serializer.using(serializer.build()))
190 .withName("onos-int-started")
191 .withApplicationId(appId)
192 .build()
193 .asAtomicValue();
194 intStarted.addListener(intStartedListener);
195
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700196 intConfig = storageService.<IntDeviceConfig>atomicValueBuilder()
Carmelo Casconefa421582018-09-13 10:05:57 -0700197 .withSerializer(Serializer.using(serializer.build()))
198 .withName("onos-int-config")
199 .withApplicationId(appId)
200 .build()
201 .asAtomicValue();
202 intConfig.addListener(intConfigListener);
203
204 intentIds = storageService.getAtomicIdGenerator("int-intent-id-generator");
205
206 // Bootstrap config for already existing devices.
207 triggerAllDeviceConfigure();
208
209 hostService.addListener(hostListener);
210 deviceService.addListener(deviceListener);
211
Yi Tsengda707962020-09-18 11:10:47 -0700212 netcfgRegistry.registerConfigFactory(intAppConfigFactory);
213 netcfgService.addListener(appConfigListener);
Yi Tsengc4f90a52020-10-04 22:33:22 -0700214 // Initialize the INT report
215 IntReportConfig reportConfig = netcfgService.getConfig(appId, IntReportConfig.class);
216 if (reportConfig != null) {
217 IntDeviceConfig intDeviceConfig = IntDeviceConfig.builder()
218 .withMinFlowHopLatencyChangeNs(reportConfig.minFlowHopLatencyChangeNs())
219 .withCollectorPort(reportConfig.collectorPort())
220 .withCollectorIp(reportConfig.collectorIp())
221 .enabled(true)
222 .build();
223 setConfig(intDeviceConfig);
224 }
Yi Tsengda707962020-09-18 11:10:47 -0700225
Carmelo Casconefa421582018-09-13 10:05:57 -0700226 startInt();
Yi Tsengc4f90a52020-10-04 22:33:22 -0700227 log.info("Started");
Carmelo Casconefa421582018-09-13 10:05:57 -0700228 }
229
230 @Deactivate
231 public void deactivate() {
232 deviceService.removeListener(deviceListener);
233 hostService.removeListener(hostListener);
234 intentIds = null;
235 intConfig.removeListener(intConfigListener);
236 intConfig = null;
237 intStarted.removeListener(intStartedListener);
238 intStarted = null;
239 intentMap.removeListener(intentMapListener);
240 intentMap = null;
241 devicesToConfigure.removeListener(devicesToConfigureListener);
242 devicesToConfigure.destroy();
243 devicesToConfigure = null;
244 // Cancel tasks (if any).
245 scheduledDeviceTasks.values().forEach(f -> {
246 f.cancel(true);
247 if (!f.isDone()) {
248 try {
249 f.get(1, TimeUnit.SECONDS);
250 } catch (InterruptedException | ExecutionException | TimeoutException e) {
251 // Don't care, we are terminating the service anyways.
252 }
253 }
254 });
255 // Clean up INT rules from existing devices.
256 deviceService.getDevices().forEach(d -> cleanupDevice(d.id()));
Yi Tsengc4f90a52020-10-04 22:33:22 -0700257 netcfgService.removeListener(appConfigListener);
258 netcfgRegistry.unregisterConfigFactory(intAppConfigFactory);
Carmelo Casconefa421582018-09-13 10:05:57 -0700259 log.info("Deactivated");
260 }
261
262 @Override
263 public void startInt() {
264 // Atomic value event will trigger device configure.
265 intStarted.set(true);
266 }
267
268 @Override
269 public void startInt(Set<DeviceId> deviceIds) {
270 log.warn("Starting INT for a subset of devices is not supported");
271 }
272
273 @Override
274 public void stopInt() {
275 // Atomic value event will trigger device configure.
276 intStarted.set(false);
277 }
278
279 @Override
280 public void stopInt(Set<DeviceId> deviceIds) {
281 log.warn("Stopping INT for a subset of devices is not supported");
282 }
283
284 @Override
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700285 public void setConfig(IntDeviceConfig cfg) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700286 checkNotNull(cfg);
287 // Atomic value event will trigger device configure.
288 intConfig.set(cfg);
289 }
290
291 @Override
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700292 public IntDeviceConfig getConfig() {
Carmelo Casconefa421582018-09-13 10:05:57 -0700293 return intConfig.get();
294 }
295
296 @Override
297 public IntIntentId installIntIntent(IntIntent intent) {
298 checkNotNull(intent);
299 final Integer intentId = (int) intentIds.nextId();
300 final IntIntentId intIntentId = IntIntentId.valueOf(intentId);
301 // Intent map event will trigger device configure.
302 intentMap.put(intIntentId, intent);
303 return intIntentId;
304 }
305
306 @Override
307 public void removeIntIntent(IntIntentId intentId) {
308 checkNotNull(intentId);
309 // Intent map event will trigger device configure.
Yi Tsengc4f90a52020-10-04 22:33:22 -0700310 if (!intentMap.containsKey(intentId)) {
311 log.warn("INT intent {} does not exists, skip removing the intent.", intentId);
312 return;
313 }
314 intentMap.remove(intentId);
Carmelo Casconefa421582018-09-13 10:05:57 -0700315 }
316
317 @Override
318 public IntIntent getIntIntent(IntIntentId intentId) {
319 return Optional.ofNullable(intentMap.get(intentId).value()).orElse(null);
320 }
321
322 @Override
323 public Map<IntIntentId, IntIntent> getIntIntents() {
324 return intentMap.asJavaMap();
325 }
326
327 private boolean isConfigTaskValid(DeviceId deviceId, long creationTime) {
328 Versioned<?> versioned = devicesToConfigure.get(deviceId);
329 return versioned != null && versioned.creationTime() == creationTime;
330 }
331
332 private boolean isIntStarted() {
333 return intStarted.get();
334 }
335
336 private boolean isNotIntConfigured() {
337 return intConfig.get() == null;
338 }
339
340 private boolean isIntProgrammable(DeviceId deviceId) {
341 final Device device = deviceService.getDevice(deviceId);
342 return device != null && device.is(IntProgrammable.class);
343 }
344
345 private void triggerDeviceConfigure(DeviceId deviceId) {
346 if (isIntProgrammable(deviceId)) {
347 devicesToConfigure.put(deviceId, System.nanoTime());
348 }
349 }
350
351 private void triggerAllDeviceConfigure() {
352 deviceService.getDevices().forEach(d -> triggerDeviceConfigure(d.id()));
353 }
354
355 private void configDeviceTask(DeviceId deviceId, long creationTime) {
356 if (isConfigTaskValid(deviceId, creationTime)) {
357 // Task outdated.
358 return;
359 }
360 if (!deviceService.isAvailable(deviceId)) {
361 return;
362 }
363 final MastershipRole role = mastershipService.requestRoleForSync(deviceId);
364 if (!role.equals(MastershipRole.MASTER)) {
365 return;
366 }
367 deviceLocks.get(deviceId).lock();
368 try {
369 // Clean up first.
370 cleanupDevice(deviceId);
371 if (!configDevice(deviceId)) {
372 // Clean up if fails.
373 cleanupDevice(deviceId);
374 return;
375 }
376 devicesToConfigure.remove(deviceId);
377 } finally {
378 deviceLocks.get(deviceId).unlock();
379 }
380 }
381
382 private void cleanupDevice(DeviceId deviceId) {
383 final Device device = deviceService.getDevice(deviceId);
384 if (device == null || !device.is(IntProgrammable.class)) {
385 return;
386 }
387 device.as(IntProgrammable.class).cleanup();
388 }
389
Yi Tsengd14f1a32020-10-13 19:15:12 -0700390 protected boolean configDevice(DeviceId deviceId) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700391 // Returns true if config was successful, false if not and a clean up is
392 // needed.
393 final Device device = deviceService.getDevice(deviceId);
394 if (device == null || !device.is(IntProgrammable.class)) {
395 return true;
396 }
397
398 if (isNotIntConfigured()) {
399 log.warn("Missing INT config, aborting programming of INT device {}", deviceId);
400 return true;
401 }
402
403 final boolean isEdge = !hostService.getConnectedHosts(deviceId).isEmpty();
Yi Tsengd14f1a32020-10-13 19:15:12 -0700404 final IntDeviceRole intDeviceRole =
405 isEdge ? IntDeviceRole.SOURCE_SINK : IntDeviceRole.TRANSIT;
Carmelo Casconefa421582018-09-13 10:05:57 -0700406
407 log.info("Started programming of INT device {} with role {}...",
Yi Tsengd14f1a32020-10-13 19:15:12 -0700408 deviceId, intDeviceRole);
Carmelo Casconefa421582018-09-13 10:05:57 -0700409
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
Yi Tsengd14f1a32020-10-13 19:15:12 -0700422 boolean supportSource = intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SOURCE);
423 boolean supportSink = intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SINK);
424 boolean supportPostcard = intProg.supportsFunctionality(IntProgrammable.IntFunctionality.POSTCARD);
425
426 if (intDeviceRole != IntDeviceRole.SOURCE_SINK && !supportPostcard) {
427 // Stop here, no more configuration needed for transit devices unless it support postcard.
Carmelo Casconefa421582018-09-13 10:05:57 -0700428 return true;
429 }
430
Yi Tsengd14f1a32020-10-13 19:15:12 -0700431 if (supportSink || supportPostcard) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700432 if (!intProg.setupIntConfig(intConfig.get())) {
433 log.warn("Unable to apply INT report config on {}", deviceId);
434 return false;
435 }
436 }
437
438 // Port configuration.
439 final Set<PortNumber> hostPorts = deviceService.getPorts(deviceId)
440 .stream()
441 .map(port -> new ConnectPoint(deviceId, port.number()))
442 .filter(cp -> !hostService.getConnectedHosts(cp).isEmpty())
443 .map(ConnectPoint::port)
444 .collect(Collectors.toSet());
445
446 for (PortNumber port : hostPorts) {
Yi Tsengd14f1a32020-10-13 19:15:12 -0700447 if (supportSource) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700448 log.info("Setting port {}/{} as INT source port...", deviceId, port);
449 if (!intProg.setSourcePort(port)) {
450 log.warn("Unable to set INT source port {} on {}", port, deviceId);
451 return false;
452 }
453 }
Yi Tsengd14f1a32020-10-13 19:15:12 -0700454 if (supportSink) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700455 log.info("Setting port {}/{} as INT sink port...", deviceId, port);
456 if (!intProg.setSinkPort(port)) {
457 log.warn("Unable to set INT sink port {} on {}", port, deviceId);
458 return false;
459 }
460 }
461 }
462
Yi Tsengd14f1a32020-10-13 19:15:12 -0700463 if (!supportSource && !supportPostcard) {
464 // Stop here, no more configuration needed for sink devices unless
465 // it supports postcard mode.
Carmelo Casconefa421582018-09-13 10:05:57 -0700466 return true;
467 }
468
469 // Apply intents.
470 // This is a trivial implementation where we simply get the
Yi Tsengd14f1a32020-10-13 19:15:12 -0700471 // corresponding INT objective from an intent and we apply to all
472 // device which support reporting.
Carmelo Casconefa421582018-09-13 10:05:57 -0700473 int appliedCount = 0;
Yi Tsengd14f1a32020-10-13 19:15:12 -0700474 for (Versioned<IntIntent> versionedIntent : intentMap.values()) {
475 IntIntent intent = versionedIntent.value();
476 IntObjective intObjective = getIntObjective(intent);
477 if (intent.telemetryMode() == IntIntent.TelemetryMode.INBAND_TELEMETRY && supportSource) {
478 intProg.addIntObjective(intObjective);
479 appliedCount++;
480 } else if (intent.telemetryMode() == IntIntent.TelemetryMode.POSTCARD && supportPostcard) {
481 intProg.addIntObjective(intObjective);
482 appliedCount++;
483 } else {
484 log.warn("Device {} does not support intent {}.", deviceId, intent);
Carmelo Casconefa421582018-09-13 10:05:57 -0700485 }
486 }
Carmelo Casconefa421582018-09-13 10:05:57 -0700487 log.info("Completed programming of {}, applied {} INT objectives of {} total",
Yi Tsengd14f1a32020-10-13 19:15:12 -0700488 deviceId, appliedCount, intentMap.size());
Carmelo Casconefa421582018-09-13 10:05:57 -0700489 return true;
490 }
491
492 private IntObjective getIntObjective(IntIntent intent) {
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700493 // FIXME: we are ignore intent.headerType()
494 // what should we do with it?
Carmelo Casconefa421582018-09-13 10:05:57 -0700495 return new IntObjective.Builder()
496 .withSelector(intent.selector())
497 .withMetadataTypes(intent.metadataTypes())
Carmelo Casconefa421582018-09-13 10:05:57 -0700498 .build();
499 }
500
501 /* Event listeners which trigger device configuration. */
502
503 private class InternalHostListener implements HostListener {
504 @Override
505 public void event(HostEvent event) {
506 final DeviceId deviceId = event.subject().location().deviceId();
507 triggerDeviceConfigure(deviceId);
508 }
509 }
510
511 private class InternalDeviceListener implements DeviceListener {
512 @Override
513 public void event(DeviceEvent event) {
514 switch (event.type()) {
515 case DEVICE_ADDED:
516 case DEVICE_UPDATED:
517 case DEVICE_REMOVED:
518 case DEVICE_SUSPENDED:
519 case DEVICE_AVAILABILITY_CHANGED:
520 case PORT_ADDED:
521 case PORT_UPDATED:
522 case PORT_REMOVED:
523 triggerDeviceConfigure(event.subject().id());
524 return;
525 case PORT_STATS_UPDATED:
526 return;
527 default:
528 log.warn("Unknown device event type {}", event.type());
529 }
530 }
531 }
532
533 private class InternalIntentMapListener
534 implements MapEventListener<IntIntentId, IntIntent> {
535 @Override
536 public void event(MapEvent<IntIntentId, IntIntent> event) {
537 triggerAllDeviceConfigure();
538 }
539 }
540
541 private class InternalIntConfigListener
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700542 implements AtomicValueEventListener<IntDeviceConfig> {
Carmelo Casconefa421582018-09-13 10:05:57 -0700543 @Override
Carmelo Casconeb330fc72020-07-17 15:27:02 -0700544 public void event(AtomicValueEvent<IntDeviceConfig> event) {
Carmelo Casconefa421582018-09-13 10:05:57 -0700545 triggerAllDeviceConfigure();
546 }
547 }
548
549 private class InternalIntStartedListener
550 implements AtomicValueEventListener<Boolean> {
551 @Override
552 public void event(AtomicValueEvent<Boolean> event) {
553 triggerAllDeviceConfigure();
554 }
555 }
556
557 private class InternalDeviceToConfigureListener
558 implements MapEventListener<DeviceId, Long> {
559 @Override
560 public void event(MapEvent<DeviceId, Long> event) {
561 if (event.type().equals(MapEvent.Type.REMOVE) ||
562 event.newValue() == null) {
563 return;
564 }
565 // Schedule task in the future. Wait for events for this device to
566 // stabilize.
567 final DeviceId deviceId = event.key();
568 final long creationTime = event.newValue().creationTime();
569 ScheduledFuture<?> newTask = SharedScheduledExecutors.newTimeout(
570 () -> configDeviceTask(deviceId, creationTime),
571 CONFIG_EVENT_DELAY, TimeUnit.SECONDS);
572 ScheduledFuture<?> oldTask = scheduledDeviceTasks.put(deviceId, newTask);
573 if (oldTask != null) {
574 oldTask.cancel(false);
575 }
576 }
577 }
Yi Tsengda707962020-09-18 11:10:47 -0700578
579 private class IntAppConfigListener implements NetworkConfigListener {
580
581 @Override
582 public void event(NetworkConfigEvent event) {
583 switch (event.type()) {
584 case CONFIG_ADDED:
585 case CONFIG_UPDATED:
586 event.config()
587 .map(config -> (IntReportConfig) config)
588 .ifPresent(config -> {
589 IntDeviceConfig intDeviceConfig = IntDeviceConfig.builder()
590 .withMinFlowHopLatencyChangeNs(config.minFlowHopLatencyChangeNs())
591 .withCollectorPort(config.collectorPort())
592 .withCollectorIp(config.collectorIp())
593 .enabled(true)
594 .build();
595 setConfig(intDeviceConfig);
Yi Tseng906febe2021-03-29 01:58:15 -0700596
597 // For each watched subnet, we install two INT rules.
598 // One match on the source, another match on the destination.
599 intentMap.clear();
600 config.watchSubnets().forEach(subnet -> {
601 IntIntent.Builder intIntentBuilder = IntIntent.builder()
602 .withReportType(IntIntent.IntReportType.TRACKED_FLOW)
603 .withReportType(IntIntent.IntReportType.DROPPED_PACKET)
604 .withReportType(IntIntent.IntReportType.CONGESTED_QUEUE)
605 .withTelemetryMode(IntIntent.TelemetryMode.POSTCARD);
606 if (subnet.prefixLength() == 0) {
607 // Special case, match any packet
608 installIntIntent(intIntentBuilder
609 .withSelector(DefaultTrafficSelector.emptySelector())
610 .build());
611 } else {
612 TrafficSelector selector = DefaultTrafficSelector.builder()
613 .matchIPSrc(subnet)
614 .build();
615 installIntIntent(intIntentBuilder.withSelector(selector).build());
616 selector = DefaultTrafficSelector.builder()
617 .matchIPDst(subnet)
618 .build();
619 installIntIntent(intIntentBuilder.withSelector(selector).build());
620 }
621 });
Yi Tsengda707962020-09-18 11:10:47 -0700622 });
623 break;
624 // TODO: Support removing INT config.
625 default:
626 break;
627 }
628 }
629
630 @Override
631 public boolean isRelevant(NetworkConfigEvent event) {
632 return event.configClass() == IntReportConfig.class;
633 }
634 }
Carmelo Casconefa421582018-09-13 10:05:57 -0700635}