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