Support install IntIntents to device with postcard mode
Include the following changes:
- INT application now can install reuls/configs to the device based on the
Int intent telemetry mode and the functionality supported by device.
- Add "Telemetry Mode" option to INT app GUI
Change-Id: I1a9938b076030497a3b2984abe66fed09c190c7d
diff --git a/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/SimpleIntManager.java b/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/SimpleIntManager.java
index 8506a40..25d6a50 100644
--- a/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/SimpleIntManager.java
+++ b/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/SimpleIntManager.java
@@ -65,7 +65,6 @@
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
-import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -101,25 +100,25 @@
private static final String APP_NAME = "org.onosproject.inbandtelemetry";
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- private CoreService coreService;
+ protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- private DeviceService deviceService;
+ protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- private StorageService storageService;
+ protected StorageService storageService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- private MastershipService mastershipService;
+ protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- private HostService hostService;
+ protected HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- private NetworkConfigService netcfgService;
+ protected NetworkConfigService netcfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- private NetworkConfigRegistry netcfgRegistry;
+ protected NetworkConfigRegistry netcfgRegistry;
private final Striped<Lock> deviceLocks = Striped.lock(10);
@@ -386,7 +385,7 @@
device.as(IntProgrammable.class).cleanup();
}
- private boolean configDevice(DeviceId deviceId) {
+ protected boolean configDevice(DeviceId deviceId) {
// Returns true if config was successful, false if not and a clean up is
// needed.
final Device device = deviceService.getDevice(deviceId);
@@ -400,12 +399,11 @@
}
final boolean isEdge = !hostService.getConnectedHosts(deviceId).isEmpty();
- final IntDeviceRole intDeviceRole = isEdge
- ? IntDeviceRole.SOURCE_SINK
- : IntDeviceRole.TRANSIT;
+ final IntDeviceRole intDeviceRole =
+ isEdge ? IntDeviceRole.SOURCE_SINK : IntDeviceRole.TRANSIT;
log.info("Started programming of INT device {} with role {}...",
- deviceId, intDeviceRole);
+ deviceId, intDeviceRole);
final IntProgrammable intProg = device.as(IntProgrammable.class);
@@ -419,12 +417,16 @@
return false;
}
- if (intDeviceRole != IntDeviceRole.SOURCE_SINK) {
- // Stop here, no more configuration needed for transit devices.
+ boolean supportSource = intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SOURCE);
+ boolean supportSink = intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SINK);
+ boolean supportPostcard = intProg.supportsFunctionality(IntProgrammable.IntFunctionality.POSTCARD);
+
+ if (intDeviceRole != IntDeviceRole.SOURCE_SINK && !supportPostcard) {
+ // Stop here, no more configuration needed for transit devices unless it support postcard.
return true;
}
- if (intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SINK)) {
+ if (supportSink || supportPostcard) {
if (!intProg.setupIntConfig(intConfig.get())) {
log.warn("Unable to apply INT report config on {}", deviceId);
return false;
@@ -440,14 +442,14 @@
.collect(Collectors.toSet());
for (PortNumber port : hostPorts) {
- if (intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SOURCE)) {
+ if (supportSource) {
log.info("Setting port {}/{} as INT source port...", deviceId, port);
if (!intProg.setSourcePort(port)) {
log.warn("Unable to set INT source port {} on {}", port, deviceId);
return false;
}
}
- if (intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SINK)) {
+ if (supportSink) {
log.info("Setting port {}/{} as INT sink port...", deviceId, port);
if (!intProg.setSinkPort(port)) {
log.warn("Unable to set INT sink port {} on {}", port, deviceId);
@@ -456,28 +458,32 @@
}
}
- if (!intProg.supportsFunctionality(IntProgrammable.IntFunctionality.SOURCE)) {
- // Stop here, no more configuration needed for sink devices.
+ if (!supportSource && !supportPostcard) {
+ // Stop here, no more configuration needed for sink devices unless
+ // it supports postcard mode.
return true;
}
// Apply intents.
// This is a trivial implementation where we simply get the
- // corresponding INT objective from an intent and we apply to all source
- // device.
- final Collection<IntObjective> objectives = intentMap.values().stream()
- .map(v -> getIntObjective(v.value()))
- .collect(Collectors.toList());
+ // corresponding INT objective from an intent and we apply to all
+ // device which support reporting.
int appliedCount = 0;
- for (IntObjective objective : objectives) {
- if (intProg.addIntObjective(objective)) {
- appliedCount = appliedCount + 1;
+ for (Versioned<IntIntent> versionedIntent : intentMap.values()) {
+ IntIntent intent = versionedIntent.value();
+ IntObjective intObjective = getIntObjective(intent);
+ if (intent.telemetryMode() == IntIntent.TelemetryMode.INBAND_TELEMETRY && supportSource) {
+ intProg.addIntObjective(intObjective);
+ appliedCount++;
+ } else if (intent.telemetryMode() == IntIntent.TelemetryMode.POSTCARD && supportPostcard) {
+ intProg.addIntObjective(intObjective);
+ appliedCount++;
+ } else {
+ log.warn("Device {} does not support intent {}.", deviceId, intent);
}
}
-
log.info("Completed programming of {}, applied {} INT objectives of {} total",
- deviceId, appliedCount, objectives.size());
-
+ deviceId, appliedCount, intentMap.size());
return true;
}
diff --git a/apps/inbandtelemetry/impl/src/test/java/org/onosproject/inbandtelemetry/impl/SimpleIntManagerTest.java b/apps/inbandtelemetry/impl/src/test/java/org/onosproject/inbandtelemetry/impl/SimpleIntManagerTest.java
index 6f7ed96..566ed2b 100644
--- a/apps/inbandtelemetry/impl/src/test/java/org/onosproject/inbandtelemetry/impl/SimpleIntManagerTest.java
+++ b/apps/inbandtelemetry/impl/src/test/java/org/onosproject/inbandtelemetry/impl/SimpleIntManagerTest.java
@@ -16,47 +16,104 @@
package org.onosproject.inbandtelemetry.impl;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
import org.onlab.packet.TpPort;
+import org.onlab.packet.VlanId;
import org.onosproject.TestApplicationId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
+import org.onosproject.inbandtelemetry.api.IntIntent;
import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultHost;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.inbandtelemetry.IntObjective;
+import org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable;
import org.onosproject.net.behaviour.inbandtelemetry.IntReportConfig;
import org.onosproject.net.behaviour.inbandtelemetry.IntDeviceConfig;
-import org.onosproject.net.config.ConfigApplyDelegate;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.host.HostService;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.TestStorageService;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.newCapture;
import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
-import static org.onlab.junit.TestUtils.setField;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable.IntFunctionality.POSTCARD;
+import static org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable.IntFunctionality.SINK;
+import static org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable.IntFunctionality.SOURCE;
+import static org.onosproject.net.behaviour.inbandtelemetry.IntProgrammable.IntFunctionality.TRANSIT;
public class SimpleIntManagerTest {
private static final String APP_NAME = "org.onosproject.inbandtelemetry";
private static final ApplicationId APP_ID = new TestApplicationId(APP_NAME);
private static final IpAddress COLLECTOR_IP = IpAddress.valueOf("10.0.0.1");
- private static final TpPort COLLECTOR_PORT = TpPort.tpPort(5500);
- private static final int MIN_FLOW_HOP_LATENCY_CHANGE_NS = 16;
+ private static final TpPort COLLECTOR_PORT = TpPort.tpPort(32766);
+ private static final int MIN_FLOW_HOP_LATENCY_CHANGE_NS = 32;
+ private static final String INT_REPORT_CONFIG_KEY = "report";
+ private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:leaf1");
+ private static final TrafficSelector FLOW_SELECTOR1 = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("192.168.10.0/24"))
+ .matchVlanId(VlanId.vlanId((short) 10))
+ .build();
+ private static final TrafficSelector FLOW_SELECTOR2 = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("192.168.20.0/24"))
+ .matchVlanId(VlanId.vlanId((short) 20))
+ .build();
+ private static final Device DEFAULT_DEVICE =
+ new DefaultDevice(null, DEVICE_ID, Device.Type.SWITCH, "", "", "", "", null);
+ private static final List<Port> DEVICE_PORTS = ImmutableList.of(
+ new DefaultPort(DEFAULT_DEVICE, PortNumber.portNumber(1), true),
+ new DefaultPort(DEFAULT_DEVICE, PortNumber.portNumber(2), true)
+ );
+ private static final Host HOST1 =
+ new DefaultHost(null, HostId.hostId("00:00:00:00:00:01/None"), null,
+ VlanId.NONE, ImmutableSet.of(), ImmutableSet.of(), true);
+ private static final Host HOST2 =
+ new DefaultHost(null, HostId.hostId("00:00:00:00:00:02/None"), null,
+ VlanId.NONE, ImmutableSet.of(), ImmutableSet.of(), true);
+ private static final Map<ConnectPoint, Host> HOSTS = ImmutableMap.of(
+ ConnectPoint.fromString("device:leaf1/1"), HOST1,
+ ConnectPoint.fromString("device:leaf1/2"), HOST2
+ );
private SimpleIntManager manager;
private StorageService storageService;
@@ -70,7 +127,7 @@
@Before
- public void setup() {
+ public void setup() throws IOException {
storageService = new TestStorageService();
mastershipService = createNiceMock(MastershipService.class);
coreService = createNiceMock(CoreService.class);
@@ -81,21 +138,26 @@
networkConfigService = createNiceMock(NetworkConfigService.class);
manager = new SimpleIntManager();
- setField(manager, "coreService", coreService);
- setField(manager, "deviceService", deviceService);
- setField(manager, "storageService", storageService);
- setField(manager, "mastershipService", mastershipService);
- setField(manager, "hostService", hostService);
- setField(manager, "netcfgService", networkConfigService);
- setField(manager, "netcfgRegistry", networkConfigRegistry);
+ manager.coreService = coreService;
+ manager.deviceService = deviceService;
+ manager.storageService = storageService;
+ manager.mastershipService = mastershipService;
+ manager.hostService = hostService;
+ manager.netcfgService = networkConfigService;
+ manager.netcfgRegistry = networkConfigRegistry;
- expect(coreService.registerApplication(APP_NAME)).andReturn(APP_ID).once();
+ expect(coreService.registerApplication(APP_NAME))
+ .andReturn(APP_ID).anyTimes();
networkConfigRegistry.registerConfigFactory(anyObject());
expectLastCall().once();
Capture<NetworkConfigListener> capture = newCapture();
networkConfigService.addListener(EasyMock.capture(capture));
expectLastCall().once();
+ IntReportConfig config = getIntReportConfig("/report-config.json");
+ expect(networkConfigService.getConfig(APP_ID, IntReportConfig.class))
+ .andReturn(config)
+ .anyTimes();
replay(mastershipService, deviceService, coreService,
hostService, networkConfigRegistry, networkConfigService);
manager.activate();
@@ -108,15 +170,8 @@
}
@Test
- public void testPushIntAppConfig() {
- IntReportConfig config = new IntReportConfig();
- ObjectMapper mapper = new ObjectMapper();
- ConfigApplyDelegate delegate = configApply -> { };
- config.init(APP_ID, "report", JsonNodeFactory.instance.objectNode(), mapper, delegate);
- config.setCollectorIp(COLLECTOR_IP)
- .setCollectorPort(COLLECTOR_PORT)
- .setMinFlowHopLatencyChangeNs(MIN_FLOW_HOP_LATENCY_CHANGE_NS);
-
+ public void testPushIntAppConfig() throws IOException {
+ IntReportConfig config = getIntReportConfig("/report-config.json");
NetworkConfigEvent event =
new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED, APP_ID,
config, null, IntReportConfig.class);
@@ -124,9 +179,199 @@
// We expected that the manager will store the device config which
// converted from the app config.
- IntDeviceConfig deviceConfig = manager.getConfig();
- assertEquals(COLLECTOR_IP, deviceConfig.collectorIp());
- assertEquals(COLLECTOR_PORT, deviceConfig.collectorPort());
- assertEquals(MIN_FLOW_HOP_LATENCY_CHANGE_NS, deviceConfig.minFlowHopLatencyChangeNs());
+ IntDeviceConfig expectedConfig = createIntDeviceConfig();
+ IntDeviceConfig actualConfig = manager.getConfig();
+ assertEquals(expectedConfig, actualConfig);
+ }
+
+ @Test
+ public void testConfigNonIntDevice() {
+ reset(deviceService);
+ Device device = getMockDevice(false, DEVICE_ID);
+ expect(deviceService.getDevice(DEVICE_ID))
+ .andReturn(device)
+ .anyTimes();
+ expect(deviceService.getDevices())
+ .andReturn(ImmutableSet.of(device))
+ .anyTimes();
+ replay(deviceService, device);
+ assertTrue(manager.configDevice(DEVICE_ID));
+ verify();
+ }
+
+ @Test
+ public void testConfigSourceDevice() {
+ reset(deviceService, hostService);
+ Device device = getMockDevice(true, DEVICE_ID);
+ IntProgrammable intProg = getMockIntProgrammable(true, false, false, false);
+ setUpDeviceTest(device, intProg, true, false);
+ IntObjective intObj = IntObjective.builder()
+ .withSelector(FLOW_SELECTOR2)
+ .build();
+ expect(intProg.addIntObjective(eq(intObj)))
+ .andReturn(true)
+ .once();
+ expect(intProg.setSourcePort(PortNumber.portNumber(1))).andReturn(true).once();
+ expect(intProg.setSourcePort(PortNumber.portNumber(2))).andReturn(true).once();
+ replay(deviceService, hostService, device, intProg);
+ installTestIntents();
+ assertTrue(manager.configDevice(DEVICE_ID));
+ verify(intProg);
+ }
+
+ @Test
+ public void testConfigTransitDevice() {
+ reset(deviceService, hostService);
+ Device device = getMockDevice(true, DEVICE_ID);
+ IntProgrammable intProg = getMockIntProgrammable(false, true, false, false);
+ setUpDeviceTest(device, intProg, false, false);
+ replay(deviceService, hostService, device, intProg);
+ installTestIntents();
+ assertTrue(manager.configDevice(DEVICE_ID));
+ verify(intProg);
+ }
+
+ @Test
+ public void testConfigSinkDevice() {
+ reset(deviceService, hostService);
+ Device device = getMockDevice(true, DEVICE_ID);
+ IntProgrammable intProg = getMockIntProgrammable(false, false, true, false);
+ setUpDeviceTest(device, intProg, true, true);
+ expect(intProg.setSinkPort(PortNumber.portNumber(1))).andReturn(true).once();
+ expect(intProg.setSinkPort(PortNumber.portNumber(2))).andReturn(true).once();
+ replay(deviceService, hostService, device, intProg);
+ installTestIntents();
+ assertTrue(manager.configDevice(DEVICE_ID));
+ verify(intProg);
+ }
+
+ @Test
+ public void testConfigPostcardOnlyDevice() {
+ reset(deviceService, hostService);
+ Device device = getMockDevice(true, DEVICE_ID);
+ IntProgrammable intProg = getMockIntProgrammable(false, false, false, true);
+ setUpDeviceTest(device, intProg, true, true);
+ IntObjective intObj = IntObjective.builder()
+ .withSelector(FLOW_SELECTOR1)
+ .build();
+ expect(intProg.addIntObjective(eq(intObj)))
+ .andReturn(true)
+ .once();
+ replay(deviceService, hostService, device, intProg);
+ installTestIntents();
+ assertTrue(manager.configDevice(DEVICE_ID));
+ verify(intProg);
+ }
+
+ /*
+ * Utilities
+ */
+ private void installTestIntents() {
+ // Pre-install an INT intent to the manager.
+ IntIntent.Builder intentBuilder = IntIntent.builder()
+ .withHeaderType(IntIntent.IntHeaderType.HOP_BY_HOP)
+ .withReportType(IntIntent.IntReportType.TRACKED_FLOW);
+
+ IntIntent postcardIntent = intentBuilder
+ .withTelemetryMode(IntIntent.TelemetryMode.POSTCARD)
+ .withSelector(FLOW_SELECTOR1)
+ .build();
+ IntIntent nonPoscardIntent = intentBuilder
+ .withTelemetryMode(IntIntent.TelemetryMode.INBAND_TELEMETRY)
+ .withSelector(FLOW_SELECTOR2)
+ .build();
+ manager.installIntIntent(nonPoscardIntent);
+ manager.installIntIntent(postcardIntent);
+ }
+
+ private void setUpDeviceTest(Device device, IntProgrammable intProg,
+ boolean hostConnected, boolean setupIntConfig) {
+ expect(device.as(IntProgrammable.class))
+ .andReturn(intProg)
+ .anyTimes();
+ expect(deviceService.getDevice(eq(DEVICE_ID)))
+ .andReturn(device)
+ .anyTimes();
+ expect(deviceService.getDevices())
+ .andReturn(ImmutableList.of(device))
+ .anyTimes();
+ if (setupIntConfig) {
+ IntDeviceConfig expectedConfig = createIntDeviceConfig();
+ expect(intProg.setupIntConfig(eq(expectedConfig)))
+ .andReturn(true)
+ .atLeastOnce();
+ }
+ expect(deviceService.getPorts(DEVICE_ID))
+ .andReturn(DEVICE_PORTS)
+ .anyTimes();
+
+ if (hostConnected) {
+ HOSTS.forEach((cp, host) -> {
+ expect(hostService.getConnectedHosts(eq(cp)))
+ .andReturn(ImmutableSet.of(host))
+ .anyTimes();
+ });
+ expect(hostService.getConnectedHosts(eq(DEVICE_ID)))
+ .andReturn(Sets.newHashSet(HOSTS.values()));
+ } else {
+ expect(hostService.getConnectedHosts(eq(DEVICE_ID)))
+ .andReturn(ImmutableSet.of())
+ .anyTimes();
+ }
+ }
+
+ private IntReportConfig getIntReportConfig(String fileName) throws IOException {
+ IntReportConfig config = new IntReportConfig();
+ InputStream jsonStream = getClass().getResourceAsStream(fileName);
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode jsonNode = mapper.readTree(jsonStream);
+ config.init(APP_ID, INT_REPORT_CONFIG_KEY, jsonNode, mapper, c -> {
+ });
+ return config;
+ }
+
+ private Device getMockDevice(boolean supportInt, DeviceId deviceId) {
+ Device device = createNiceMock(Device.class);
+ expect(device.is(IntProgrammable.class))
+ .andReturn(supportInt)
+ .anyTimes();
+ expect(device.id())
+ .andReturn(deviceId)
+ .anyTimes();
+ return device;
+ }
+
+ private IntProgrammable getMockIntProgrammable(boolean supportSource, boolean supportTransit, boolean supportSink,
+ boolean supportPostcard) {
+ IntProgrammable intProg = createNiceMock(IntProgrammable.class);
+ if (supportSource) {
+ expect(intProg.supportsFunctionality(SOURCE))
+ .andReturn(true).anyTimes();
+ }
+ if (supportTransit) {
+ expect(intProg.supportsFunctionality(TRANSIT))
+ .andReturn(true).anyTimes();
+ }
+ if (supportSink) {
+ expect(intProg.supportsFunctionality(SINK))
+ .andReturn(true).anyTimes();
+ }
+ if (supportPostcard) {
+ expect(intProg.supportsFunctionality(POSTCARD))
+ .andReturn(true).anyTimes();
+ }
+ expect(intProg.init())
+ .andReturn(true)
+ .anyTimes();
+ return intProg;
+ }
+
+ private IntDeviceConfig createIntDeviceConfig() {
+ return IntDeviceConfig.builder()
+ .withMinFlowHopLatencyChangeNs(MIN_FLOW_HOP_LATENCY_CHANGE_NS)
+ .withCollectorPort(COLLECTOR_PORT)
+ .withCollectorIp(COLLECTOR_IP)
+ .enabled(true)
+ .build();
}
}
diff --git a/apps/inbandtelemetry/impl/src/test/resources/report-config.json b/apps/inbandtelemetry/impl/src/test/resources/report-config.json
new file mode 100644
index 0000000..c0c1ca2
--- /dev/null
+++ b/apps/inbandtelemetry/impl/src/test/resources/report-config.json
@@ -0,0 +1,5 @@
+{
+ "collectorIp": "10.0.0.1",
+ "collectorPort": 32766,
+ "minFlowHopLatencyChangeNs": 32
+}
\ No newline at end of file