blob: 7fddf3cbb49d2e77d7f8ca6a524d34bd33d20890 [file] [log] [blame]
Yi Tsengda707962020-09-18 11:10:47 -07001/*
2 * Copyright 2020-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
18
Yi Tsengd14f1a32020-10-13 19:15:12 -070019import com.fasterxml.jackson.databind.JsonNode;
Yi Tsengda707962020-09-18 11:10:47 -070020import com.fasterxml.jackson.databind.ObjectMapper;
Yi Tsengda707962020-09-18 11:10:47 -070021import com.google.common.collect.ImmutableList;
Yi Tsengd14f1a32020-10-13 19:15:12 -070022import com.google.common.collect.ImmutableMap;
23import com.google.common.collect.ImmutableSet;
24import com.google.common.collect.Sets;
pierventre52ef9332021-07-09 22:42:17 +020025import com.google.common.util.concurrent.MoreExecutors;
Yi Tsengda707962020-09-18 11:10:47 -070026import org.easymock.Capture;
27import org.easymock.EasyMock;
28import org.junit.After;
29import org.junit.Before;
30import org.junit.Test;
Yi Tseng83a640a2021-03-29 01:58:15 -070031import org.onlab.junit.TestUtils;
Yi Tsengda707962020-09-18 11:10:47 -070032import org.onlab.packet.IpAddress;
Yi Tsengd14f1a32020-10-13 19:15:12 -070033import org.onlab.packet.IpPrefix;
Yi Tsengda707962020-09-18 11:10:47 -070034import org.onlab.packet.TpPort;
Yi Tsengd14f1a32020-10-13 19:15:12 -070035import org.onlab.packet.VlanId;
Yi Tsengda707962020-09-18 11:10:47 -070036import org.onosproject.TestApplicationId;
37import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
Yi Tsengd14f1a32020-10-13 19:15:12 -070039import org.onosproject.inbandtelemetry.api.IntIntent;
Yi Tseng83a640a2021-03-29 01:58:15 -070040import org.onosproject.inbandtelemetry.api.IntIntentId;
Yi Tsengda707962020-09-18 11:10:47 -070041import org.onosproject.mastership.MastershipService;
Yi Tsengd14f1a32020-10-13 19:15:12 -070042import org.onosproject.net.ConnectPoint;
43import org.onosproject.net.DefaultDevice;
44import org.onosproject.net.DefaultHost;
45import org.onosproject.net.DefaultPort;
46import org.onosproject.net.Device;
47import org.onosproject.net.DeviceId;
48import org.onosproject.net.Host;
49import org.onosproject.net.HostId;
50import org.onosproject.net.Port;
51import org.onosproject.net.PortNumber;
52import org.onosproject.net.behaviour.inbandtelemetry.IntObjective;
53import org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable;
Yi Tsengda707962020-09-18 11:10:47 -070054import org.onosproject.net.behaviour.inbandtelemetry.IntReportConfig;
55import org.onosproject.net.behaviour.inbandtelemetry.IntDeviceConfig;
Yi Tsengda707962020-09-18 11:10:47 -070056import org.onosproject.net.config.NetworkConfigEvent;
57import org.onosproject.net.config.NetworkConfigListener;
58import org.onosproject.net.config.NetworkConfigRegistry;
59import org.onosproject.net.config.NetworkConfigService;
60import org.onosproject.net.device.DeviceService;
Yi Tsengd14f1a32020-10-13 19:15:12 -070061import org.onosproject.net.flow.DefaultTrafficSelector;
62import org.onosproject.net.flow.TrafficSelector;
Yi Tsengda707962020-09-18 11:10:47 -070063import org.onosproject.net.host.HostService;
Yi Tseng83a640a2021-03-29 01:58:15 -070064import org.onosproject.store.service.ConsistentMap;
Yi Tsengda707962020-09-18 11:10:47 -070065import org.onosproject.store.service.StorageService;
66import org.onosproject.store.service.TestStorageService;
67
Yi Tsengd14f1a32020-10-13 19:15:12 -070068import java.io.IOException;
69import java.io.InputStream;
70import java.util.List;
71import java.util.Map;
Yi Tseng83a640a2021-03-29 01:58:15 -070072import java.util.Set;
Yi Tsengd14f1a32020-10-13 19:15:12 -070073
Yi Tsengda707962020-09-18 11:10:47 -070074import static org.easymock.EasyMock.anyObject;
75import static org.easymock.EasyMock.createNiceMock;
Yi Tsengd14f1a32020-10-13 19:15:12 -070076import static org.easymock.EasyMock.eq;
Yi Tsengda707962020-09-18 11:10:47 -070077import static org.easymock.EasyMock.expect;
78import static org.easymock.EasyMock.expectLastCall;
79import static org.easymock.EasyMock.newCapture;
80import static org.easymock.EasyMock.replay;
Yi Tsengd14f1a32020-10-13 19:15:12 -070081import static org.easymock.EasyMock.reset;
82import static org.easymock.EasyMock.verify;
Yi Tsengda707962020-09-18 11:10:47 -070083import static org.junit.Assert.assertEquals;
Yi Tsengd14f1a32020-10-13 19:15:12 -070084import static org.junit.Assert.assertTrue;
pierventre52ef9332021-07-09 22:42:17 +020085import static org.onlab.junit.TestTools.assertAfter;
Yi Tsengd14f1a32020-10-13 19:15:12 -070086import static org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable.IntFunctionality.POSTCARD;
87import static org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable.IntFunctionality.SINK;
88import static org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable.IntFunctionality.SOURCE;
89import static org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable.IntFunctionality.TRANSIT;
Yi Tsengda707962020-09-18 11:10:47 -070090
91public class SimpleIntManagerTest {
92 private static final String APP_NAME = "org.onosproject.inbandtelemetry";
93 private static final ApplicationId APP_ID = new TestApplicationId(APP_NAME);
94 private static final IpAddress COLLECTOR_IP = IpAddress.valueOf("10.0.0.1");
Yi Tsengd14f1a32020-10-13 19:15:12 -070095 private static final TpPort COLLECTOR_PORT = TpPort.tpPort(32766);
96 private static final int MIN_FLOW_HOP_LATENCY_CHANGE_NS = 32;
97 private static final String INT_REPORT_CONFIG_KEY = "report";
98 private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:leaf1");
Yi Tseng83a640a2021-03-29 01:58:15 -070099 private static final String WATCHED_SUBNET_1 = "192.168.10.0/24";
100 private static final String WATCHED_SUBNET_2 = "192.168.20.0/24";
Yi Tsengd14f1a32020-10-13 19:15:12 -0700101 private static final TrafficSelector FLOW_SELECTOR1 = DefaultTrafficSelector.builder()
Yi Tseng83a640a2021-03-29 01:58:15 -0700102 .matchIPDst(IpPrefix.valueOf(WATCHED_SUBNET_1))
Yi Tsengd14f1a32020-10-13 19:15:12 -0700103 .matchVlanId(VlanId.vlanId((short) 10))
104 .build();
105 private static final TrafficSelector FLOW_SELECTOR2 = DefaultTrafficSelector.builder()
Yi Tseng83a640a2021-03-29 01:58:15 -0700106 .matchIPDst(IpPrefix.valueOf(WATCHED_SUBNET_2))
Yi Tsengd14f1a32020-10-13 19:15:12 -0700107 .matchVlanId(VlanId.vlanId((short) 20))
108 .build();
109 private static final Device DEFAULT_DEVICE =
110 new DefaultDevice(null, DEVICE_ID, Device.Type.SWITCH, "", "", "", "", null);
111 private static final List<Port> DEVICE_PORTS = ImmutableList.of(
112 new DefaultPort(DEFAULT_DEVICE, PortNumber.portNumber(1), true),
113 new DefaultPort(DEFAULT_DEVICE, PortNumber.portNumber(2), true)
114 );
115 private static final Host HOST1 =
116 new DefaultHost(null, HostId.hostId("00:00:00:00:00:01/None"), null,
117 VlanId.NONE, ImmutableSet.of(), ImmutableSet.of(), true);
118 private static final Host HOST2 =
119 new DefaultHost(null, HostId.hostId("00:00:00:00:00:02/None"), null,
120 VlanId.NONE, ImmutableSet.of(), ImmutableSet.of(), true);
121 private static final Map<ConnectPoint, Host> HOSTS = ImmutableMap.of(
122 ConnectPoint.fromString("device:leaf1/1"), HOST1,
123 ConnectPoint.fromString("device:leaf1/2"), HOST2
124 );
Yi Tsengda707962020-09-18 11:10:47 -0700125
126 private SimpleIntManager manager;
127 private StorageService storageService;
128 private MastershipService mastershipService;
129 private CoreService coreService;
130 private HostService hostService;
131 private DeviceService deviceService;
132 private NetworkConfigRegistry networkConfigRegistry;
133 private NetworkConfigService networkConfigService;
134 private NetworkConfigListener networkConfigListener;
135
136
137 @Before
Yi Tsengd14f1a32020-10-13 19:15:12 -0700138 public void setup() throws IOException {
Yi Tsengda707962020-09-18 11:10:47 -0700139 storageService = new TestStorageService();
140 mastershipService = createNiceMock(MastershipService.class);
141 coreService = createNiceMock(CoreService.class);
142 hostService = createNiceMock(HostService.class);
143 deviceService = createNiceMock(DeviceService.class);
144 expect(deviceService.getDevices()).andReturn(ImmutableList.of()).anyTimes();
145 networkConfigRegistry = createNiceMock(NetworkConfigRegistry.class);
146 networkConfigService = createNiceMock(NetworkConfigService.class);
147
148 manager = new SimpleIntManager();
Yi Tsengd14f1a32020-10-13 19:15:12 -0700149 manager.coreService = coreService;
150 manager.deviceService = deviceService;
151 manager.storageService = storageService;
152 manager.mastershipService = mastershipService;
153 manager.hostService = hostService;
154 manager.netcfgService = networkConfigService;
155 manager.netcfgRegistry = networkConfigRegistry;
pierventre52ef9332021-07-09 22:42:17 +0200156 manager.eventExecutor = MoreExecutors.newDirectExecutorService();
Yi Tsengda707962020-09-18 11:10:47 -0700157
Yi Tsengd14f1a32020-10-13 19:15:12 -0700158 expect(coreService.registerApplication(APP_NAME))
159 .andReturn(APP_ID).anyTimes();
Yi Tsengda707962020-09-18 11:10:47 -0700160 networkConfigRegistry.registerConfigFactory(anyObject());
161 expectLastCall().once();
162
163 Capture<NetworkConfigListener> capture = newCapture();
164 networkConfigService.addListener(EasyMock.capture(capture));
165 expectLastCall().once();
Yi Tsengd14f1a32020-10-13 19:15:12 -0700166 IntReportConfig config = getIntReportConfig("/report-config.json");
167 expect(networkConfigService.getConfig(APP_ID, IntReportConfig.class))
168 .andReturn(config)
169 .anyTimes();
Yi Tsengda707962020-09-18 11:10:47 -0700170 replay(mastershipService, deviceService, coreService,
171 hostService, networkConfigRegistry, networkConfigService);
172 manager.activate();
173 networkConfigListener = capture.getValue();
174 }
175
176 @After
177 public void teardown() {
178 manager.deactivate();
179 }
180
181 @Test
Yi Tsengd14f1a32020-10-13 19:15:12 -0700182 public void testPushIntAppConfig() throws IOException {
183 IntReportConfig config = getIntReportConfig("/report-config.json");
Yi Tsengda707962020-09-18 11:10:47 -0700184 NetworkConfigEvent event =
185 new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED, APP_ID,
186 config, null, IntReportConfig.class);
187 networkConfigListener.event(event);
188
189 // We expected that the manager will store the device config which
190 // converted from the app config.
Yi Tsengd14f1a32020-10-13 19:15:12 -0700191 IntDeviceConfig expectedConfig = createIntDeviceConfig();
192 IntDeviceConfig actualConfig = manager.getConfig();
193 assertEquals(expectedConfig, actualConfig);
Yi Tseng83a640a2021-03-29 01:58:15 -0700194
195 // Install watch subnets via netcfg
196 // In the report-config.json, there are 3 subnets we want to watch
197 // For subnet 0.0.0.0/0, the IntManager will create only one IntIntent with an empty selector.
198 Set<IntIntent> expectedIntIntents = Sets.newHashSet();
199 ConsistentMap<IntIntentId, IntIntent> intentMap = TestUtils.getField(manager, "intentMap");
200 IntIntent.Builder baseIntentBuilder = IntIntent.builder()
201 .withReportType(IntIntent.IntReportType.TRACKED_FLOW)
202 .withReportType(IntIntent.IntReportType.DROPPED_PACKET)
203 .withReportType(IntIntent.IntReportType.CONGESTED_QUEUE)
204 .withTelemetryMode(IntIntent.TelemetryMode.POSTCARD);
205
206 // Watch IP Src == subnet 1
207 TrafficSelector expectedSelector = DefaultTrafficSelector.builder()
208 .matchIPSrc(IpPrefix.valueOf(WATCHED_SUBNET_1))
209 .build();
210 expectedIntIntents.add(baseIntentBuilder.withSelector(expectedSelector).build());
211 // Watch IP Dst == subnet 1
212 expectedSelector = DefaultTrafficSelector.builder()
213 .matchIPDst(IpPrefix.valueOf(WATCHED_SUBNET_1))
214 .build();
215 expectedIntIntents.add(baseIntentBuilder.withSelector(expectedSelector).build());
216 // Watch IP Src == subnet 2
217 expectedSelector = DefaultTrafficSelector.builder()
218 .matchIPSrc(IpPrefix.valueOf(WATCHED_SUBNET_2))
219 .build();
220 expectedIntIntents.add(baseIntentBuilder.withSelector(expectedSelector).build());
221 // Watch IP Dst == subnet 2
222 expectedSelector = DefaultTrafficSelector.builder()
223 .matchIPDst(IpPrefix.valueOf(WATCHED_SUBNET_2))
224 .build();
225 expectedIntIntents.add(baseIntentBuilder.withSelector(expectedSelector).build());
226 // Any packets
227 expectedSelector = DefaultTrafficSelector.emptySelector();
228 expectedIntIntents.add(baseIntentBuilder.withSelector(expectedSelector).build());
229
230 // The INT intent installation order can be random, so we need to collect
231 // all expected INT intents and check if actual intent exists.
pierventre52ef9332021-07-09 22:42:17 +0200232 assertAfter(50, 100, () -> assertEquals(5, intentMap.size()));
Yi Tseng83a640a2021-03-29 01:58:15 -0700233 intentMap.entrySet().forEach(entry -> {
234 IntIntent actualIntIntent = entry.getValue().value();
235 assertTrue(expectedIntIntents.contains(actualIntIntent));
236 });
Yi Tsengd14f1a32020-10-13 19:15:12 -0700237 }
238
239 @Test
240 public void testConfigNonIntDevice() {
241 reset(deviceService);
242 Device device = getMockDevice(false, DEVICE_ID);
243 expect(deviceService.getDevice(DEVICE_ID))
244 .andReturn(device)
245 .anyTimes();
246 expect(deviceService.getDevices())
247 .andReturn(ImmutableSet.of(device))
248 .anyTimes();
249 replay(deviceService, device);
250 assertTrue(manager.configDevice(DEVICE_ID));
251 verify();
252 }
253
254 @Test
255 public void testConfigSourceDevice() {
256 reset(deviceService, hostService);
257 Device device = getMockDevice(true, DEVICE_ID);
258 IntProgrammable intProg = getMockIntProgrammable(true, false, false, false);
259 setUpDeviceTest(device, intProg, true, false);
260 IntObjective intObj = IntObjective.builder()
261 .withSelector(FLOW_SELECTOR2)
262 .build();
263 expect(intProg.addIntObjective(eq(intObj)))
264 .andReturn(true)
265 .once();
266 expect(intProg.setSourcePort(PortNumber.portNumber(1))).andReturn(true).once();
267 expect(intProg.setSourcePort(PortNumber.portNumber(2))).andReturn(true).once();
268 replay(deviceService, hostService, device, intProg);
269 installTestIntents();
270 assertTrue(manager.configDevice(DEVICE_ID));
271 verify(intProg);
272 }
273
274 @Test
275 public void testConfigTransitDevice() {
276 reset(deviceService, hostService);
277 Device device = getMockDevice(true, DEVICE_ID);
278 IntProgrammable intProg = getMockIntProgrammable(false, true, false, false);
279 setUpDeviceTest(device, intProg, false, false);
280 replay(deviceService, hostService, device, intProg);
281 installTestIntents();
282 assertTrue(manager.configDevice(DEVICE_ID));
283 verify(intProg);
284 }
285
286 @Test
287 public void testConfigSinkDevice() {
288 reset(deviceService, hostService);
289 Device device = getMockDevice(true, DEVICE_ID);
290 IntProgrammable intProg = getMockIntProgrammable(false, false, true, false);
291 setUpDeviceTest(device, intProg, true, true);
292 expect(intProg.setSinkPort(PortNumber.portNumber(1))).andReturn(true).once();
293 expect(intProg.setSinkPort(PortNumber.portNumber(2))).andReturn(true).once();
294 replay(deviceService, hostService, device, intProg);
295 installTestIntents();
296 assertTrue(manager.configDevice(DEVICE_ID));
297 verify(intProg);
298 }
299
300 @Test
301 public void testConfigPostcardOnlyDevice() {
302 reset(deviceService, hostService);
303 Device device = getMockDevice(true, DEVICE_ID);
304 IntProgrammable intProg = getMockIntProgrammable(false, false, false, true);
305 setUpDeviceTest(device, intProg, true, true);
306 IntObjective intObj = IntObjective.builder()
307 .withSelector(FLOW_SELECTOR1)
308 .build();
309 expect(intProg.addIntObjective(eq(intObj)))
310 .andReturn(true)
311 .once();
312 replay(deviceService, hostService, device, intProg);
313 installTestIntents();
314 assertTrue(manager.configDevice(DEVICE_ID));
315 verify(intProg);
316 }
317
318 /*
319 * Utilities
320 */
321 private void installTestIntents() {
322 // Pre-install an INT intent to the manager.
323 IntIntent.Builder intentBuilder = IntIntent.builder()
324 .withHeaderType(IntIntent.IntHeaderType.HOP_BY_HOP)
325 .withReportType(IntIntent.IntReportType.TRACKED_FLOW);
326
327 IntIntent postcardIntent = intentBuilder
328 .withTelemetryMode(IntIntent.TelemetryMode.POSTCARD)
329 .withSelector(FLOW_SELECTOR1)
330 .build();
331 IntIntent nonPoscardIntent = intentBuilder
332 .withTelemetryMode(IntIntent.TelemetryMode.INBAND_TELEMETRY)
333 .withSelector(FLOW_SELECTOR2)
334 .build();
335 manager.installIntIntent(nonPoscardIntent);
336 manager.installIntIntent(postcardIntent);
337 }
338
339 private void setUpDeviceTest(Device device, IntProgrammable intProg,
340 boolean hostConnected, boolean setupIntConfig) {
341 expect(device.as(IntProgrammable.class))
342 .andReturn(intProg)
343 .anyTimes();
344 expect(deviceService.getDevice(eq(DEVICE_ID)))
345 .andReturn(device)
346 .anyTimes();
347 expect(deviceService.getDevices())
348 .andReturn(ImmutableList.of(device))
349 .anyTimes();
350 if (setupIntConfig) {
351 IntDeviceConfig expectedConfig = createIntDeviceConfig();
352 expect(intProg.setupIntConfig(eq(expectedConfig)))
353 .andReturn(true)
354 .atLeastOnce();
355 }
356 expect(deviceService.getPorts(DEVICE_ID))
357 .andReturn(DEVICE_PORTS)
358 .anyTimes();
359
360 if (hostConnected) {
361 HOSTS.forEach((cp, host) -> {
362 expect(hostService.getConnectedHosts(eq(cp)))
363 .andReturn(ImmutableSet.of(host))
364 .anyTimes();
365 });
366 expect(hostService.getConnectedHosts(eq(DEVICE_ID)))
367 .andReturn(Sets.newHashSet(HOSTS.values()));
368 } else {
369 expect(hostService.getConnectedHosts(eq(DEVICE_ID)))
370 .andReturn(ImmutableSet.of())
371 .anyTimes();
372 }
373 }
374
375 private IntReportConfig getIntReportConfig(String fileName) throws IOException {
376 IntReportConfig config = new IntReportConfig();
377 InputStream jsonStream = getClass().getResourceAsStream(fileName);
378 ObjectMapper mapper = new ObjectMapper();
379 JsonNode jsonNode = mapper.readTree(jsonStream);
380 config.init(APP_ID, INT_REPORT_CONFIG_KEY, jsonNode, mapper, c -> {
381 });
382 return config;
383 }
384
385 private Device getMockDevice(boolean supportInt, DeviceId deviceId) {
386 Device device = createNiceMock(Device.class);
387 expect(device.is(IntProgrammable.class))
388 .andReturn(supportInt)
389 .anyTimes();
390 expect(device.id())
391 .andReturn(deviceId)
392 .anyTimes();
393 return device;
394 }
395
396 private IntProgrammable getMockIntProgrammable(boolean supportSource, boolean supportTransit, boolean supportSink,
397 boolean supportPostcard) {
398 IntProgrammable intProg = createNiceMock(IntProgrammable.class);
399 if (supportSource) {
400 expect(intProg.supportsFunctionality(SOURCE))
401 .andReturn(true).anyTimes();
402 }
403 if (supportTransit) {
404 expect(intProg.supportsFunctionality(TRANSIT))
405 .andReturn(true).anyTimes();
406 }
407 if (supportSink) {
408 expect(intProg.supportsFunctionality(SINK))
409 .andReturn(true).anyTimes();
410 }
411 if (supportPostcard) {
412 expect(intProg.supportsFunctionality(POSTCARD))
413 .andReturn(true).anyTimes();
414 }
415 expect(intProg.init())
416 .andReturn(true)
417 .anyTimes();
418 return intProg;
419 }
420
421 private IntDeviceConfig createIntDeviceConfig() {
422 return IntDeviceConfig.builder()
423 .withMinFlowHopLatencyChangeNs(MIN_FLOW_HOP_LATENCY_CHANGE_NS)
424 .withCollectorPort(COLLECTOR_PORT)
425 .withCollectorIp(COLLECTOR_IP)
426 .enabled(true)
427 .build();
Yi Tsengda707962020-09-18 11:10:47 -0700428 }
429}