Initial implementation of IntService and sample application
This implementation has been used for P4Workshop demo (ONOS INT Support).
Change-Id: I2ff94f8a79f6d5a328b94f7ed178e575b81c3fe9
diff --git a/apps/inbandtelemetry/app/BUCK b/apps/inbandtelemetry/app/BUCK
new file mode 100644
index 0000000..a5d7b9e
--- /dev/null
+++ b/apps/inbandtelemetry/app/BUCK
@@ -0,0 +1,32 @@
+BUNDLES = [
+ '//apps/inbandtelemetry/api:onos-apps-inbandtelemetry-api',
+ '//apps/inbandtelemetry/impl:onos-apps-inbandtelemetry-impl',
+ '//apps/inbandtelemetry/app:onos-apps-inbandtelemetry-app',
+]
+
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//lib:KRYO',
+ '//lib:JACKSON',
+ '//core/store/serializers:onos-core-serializers',
+ '//apps/inbandtelemetry/api:onos-apps-inbandtelemetry-api',
+]
+
+TEST_DEPS = [
+ '//lib:TEST_REST',
+]
+
+osgi_jar_with_tests(
+ deps = COMPILE_DEPS,
+ test_deps = TEST_DEPS,
+)
+
+onos_app (
+ app_name = 'org.onosproject.inbandtelemetry.app',
+ title = 'P4 In-band Network Telemetry Sample Application',
+ category = 'Monitoring',
+ url = 'http://onosproject.org',
+ description = 'Provides managements of INT-capable devices. Specifies flows to enable INT and' +
+ 'types of metadata to collect. Sets up INT-related information.',
+ included_bundles = BUNDLES,
+)
\ No newline at end of file
diff --git a/apps/inbandtelemetry/app/pom.xml b/apps/inbandtelemetry/app/pom.xml
new file mode 100644
index 0000000..e397330
--- /dev/null
+++ b/apps/inbandtelemetry/app/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016-present Open Networking Foundation
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>onos-apps-inbandtelemetry</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.14.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-apps-inbandtelemetry-app</artifactId>
+ <packaging>bundle</packaging>
+
+ <url>http://onosproject.org</url>
+
+ <description>P4 INT Management Application</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps-inbandtelemetry-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-core-serializers</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/IntControl.java b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/IntControl.java
new file mode 100644
index 0000000..efd3590
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/IntControl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.inbandtelemetry.app;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.core.CoreService;
+import org.onosproject.inbandtelemetry.api.IntService;
+import org.onosproject.net.host.HostService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(immediate = true)
+public class IntControl {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+// private ApplicationId appId;
+// private static final int collectorPort = 1234;
+// private static final IpAddress collectorIp = IpAddress.valueOf("10.0.0.3");
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntService intService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Activate
+ protected void activate() {
+ coreService.registerApplication("org.onosproject.inbandtelemetry.app");
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ log.info("Stopped");
+ }
+}
diff --git a/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/package-info.java b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/package-info.java
new file mode 100644
index 0000000..859bdf7
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * IntService sample application.
+ */
+package org.onosproject.inbandtelemetry.app;
diff --git a/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/IntAppTableMessageHandler.java b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/IntAppTableMessageHandler.java
new file mode 100644
index 0000000..fafd4b4
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/IntAppTableMessageHandler.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.inbandtelemetry.app.ui;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.inbandtelemetry.api.IntIntent;
+import org.onosproject.inbandtelemetry.api.IntIntentId;
+import org.onosproject.inbandtelemetry.api.IntService;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.TcpPortCriterion;
+import org.onosproject.net.flow.criteria.UdpPortCriterion;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.TableModel;
+import org.onosproject.ui.table.TableRequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Message handler for installed INT intents table in the application UI.
+ */
+public class IntAppTableMessageHandler extends UiMessageHandler {
+ private static final String INT_APP_INT_INTENT = "intAppIntIntent";
+ private static final String INT_APP_INT_INTENT_DATA_REQUEST = INT_APP_INT_INTENT + "DataRequest";
+ private static final String INT_APP_INT_INTENT_DATA_RESPONSE = INT_APP_INT_INTENT + "DataResponse";
+
+ private static final String INT_APP_DEL_INT_INTENT_REQ = "intAppDelIntIntentRequest";
+
+ private static final String NO_ROWS_MESSAGE = "No IntIntent found";
+
+ private static final String ID = "id";
+ private static final String SRC_ADDR = "srcAddr";
+ private static final String DST_ADDR = "dstAddr";
+ private static final String SRC_PORT = "srcPort";
+ private static final String DST_PORT = "dstPort";
+ private static final String PROTOCOL = "protocol";
+ private static final String METADATA = "metadata";
+
+ private static final String[] COLUMN_IDS = {ID, SRC_ADDR, DST_ADDR, SRC_PORT, DST_PORT, PROTOCOL, METADATA};
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ protected IntService intService;
+
+ @Override
+ protected Collection<RequestHandler> createRequestHandlers() {
+ return ImmutableSet.of(
+ new IntAppIntIntentRequestHandler(),
+ new IntAppDelIntIntentRequestHandler()
+ );
+ }
+
+ // handler for table requests
+ private final class IntAppIntIntentRequestHandler extends TableRequestHandler {
+
+ private IntAppIntIntentRequestHandler() {
+ super(INT_APP_INT_INTENT_DATA_REQUEST, INT_APP_INT_INTENT_DATA_RESPONSE, INT_APP_INT_INTENT);
+ }
+
+ @Override
+ protected String[] getColumnIds() {
+ return COLUMN_IDS;
+ }
+
+ @Override
+ protected String noRowsMessage(ObjectNode payload) {
+ return NO_ROWS_MESSAGE;
+ }
+
+ private Map<IntIntentId, IntIntent> getAllIntIntents() {
+ intService = get(IntService.class);
+ return intService.getIntIntents();
+ }
+
+ @Override
+ protected void populateTable(TableModel tm, ObjectNode payload) {
+ Map<IntIntentId, IntIntent> intentMap = getAllIntIntents();
+ intentMap.entrySet().forEach(entry ->
+ populateRow(tm.addRow(), entry.getKey(), entry.getValue()));
+ }
+
+ private void populateRow(TableModel.Row row, IntIntentId intentId, IntIntent intent) {
+ IPCriterion ip4Src = (IPCriterion) intent.selector().getCriterion(Criterion.Type.IPV4_SRC);
+ IPCriterion ip4Dst = (IPCriterion) intent.selector().getCriterion(Criterion.Type.IPV4_DST);
+ TcpPortCriterion tcpSrcPort = (TcpPortCriterion) intent.selector().getCriterion(Criterion.Type.TCP_SRC);
+ TcpPortCriterion tcpDstPort = (TcpPortCriterion) intent.selector().getCriterion(Criterion.Type.TCP_DST);
+ UdpPortCriterion udpSrcPort = (UdpPortCriterion) intent.selector().getCriterion(Criterion.Type.UDP_SRC);
+ UdpPortCriterion udpDstPort = (UdpPortCriterion) intent.selector().getCriterion(Criterion.Type.UDP_DST);
+ Set<IntIntent.IntMetadataType> metadataTypes = intent.metadataTypes();
+ row.cell(ID, intentId.toString())
+ .cell(SRC_ADDR, ip4Src == null ? "N/A" : ip4Src.ip().toString())
+ .cell(DST_ADDR, ip4Dst == null ? "N/A" : ip4Dst.ip().toString());
+ if (tcpSrcPort != null || tcpDstPort != null) {
+ row.cell(PROTOCOL, "TCP")
+ .cell(SRC_PORT, tcpSrcPort == null ? "N/A" : tcpSrcPort.tcpPort().toString())
+ .cell(DST_PORT, tcpDstPort == null ? "N/A" : tcpDstPort.tcpPort().toString());
+ } else if (udpSrcPort != null || udpDstPort != null) {
+ row.cell(PROTOCOL, "UDP")
+ .cell(SRC_PORT, udpSrcPort == null ? "N/A" : udpSrcPort.udpPort().toString())
+ .cell(DST_PORT, udpDstPort == null ? "N/A" : udpDstPort.udpPort().toString());
+ } else {
+ row.cell(PROTOCOL, "N/A")
+ .cell(SRC_PORT, "N/A")
+ .cell(DST_PORT, "N/A");
+ }
+ String metaStr = "";
+ for (IntIntent.IntMetadataType metadataType : metadataTypes) {
+ metaStr += metadataType.toString();
+ metaStr += ", ";
+ }
+ row.cell(METADATA, metaStr);
+ }
+ }
+
+ private final class IntAppDelIntIntentRequestHandler extends RequestHandler {
+
+ private IntAppDelIntIntentRequestHandler() {
+ super(INT_APP_DEL_INT_INTENT_REQ);
+ }
+
+ @Override
+ public void process(ObjectNode payload) {
+ intService = get(IntService.class);
+ if (payload.get(ID) != null) {
+ intService.removeIntIntent(IntIntentId.valueOf(payload.get(ID).asLong()));
+ }
+ }
+ }
+}
diff --git a/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/IntAppUiComponent.java b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/IntAppUiComponent.java
new file mode 100644
index 0000000..94c823e
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/IntAppUiComponent.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.inbandtelemetry.app.ui;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.ui.UiExtension;
+import org.onosproject.ui.UiExtensionService;
+import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiView;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Skeletal ONOS UI Custom-View application component.
+ */
+@Component(immediate = true)
+public class IntAppUiComponent {
+
+ private static final String VIEW_ID = "intApp";
+ private static final String VIEW_TEXT = "In-band Telemetry Control";
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected UiExtensionService uiExtensionService;
+
+ // List of application views
+ private final List<UiView> uiViews = ImmutableList.of(
+ new UiView(UiView.Category.OTHER, VIEW_ID, VIEW_TEXT)
+ );
+
+ // Factory for UI message handlers
+ private final UiMessageHandlerFactory messageHandlerFactory =
+ () -> ImmutableList.of(
+ new IntAppUiMessageHandler(),
+ new IntAppTableMessageHandler()
+ );
+
+ // Application UI extension
+ protected UiExtension extension =
+ new UiExtension.Builder(getClass().getClassLoader(), uiViews)
+ .resourcePath(VIEW_ID)
+ .messageHandlerFactory(messageHandlerFactory)
+ .build();
+
+ @Activate
+ protected void activate() {
+ uiExtensionService.register(extension);
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ uiExtensionService.unregister(extension);
+ log.info("Stopped");
+ }
+}
diff --git a/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/IntAppUiMessageHandler.java b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/IntAppUiMessageHandler.java
new file mode 100644
index 0000000..5a81c60
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/IntAppUiMessageHandler.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.inbandtelemetry.app.ui;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.ImmutableSet;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.inbandtelemetry.api.IntIntent;
+import org.onosproject.inbandtelemetry.api.IntIntentId;
+import org.onosproject.inbandtelemetry.api.IntService;
+import org.onosproject.inbandtelemetry.api.IntConfig;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiMessageHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+
+public class IntAppUiMessageHandler extends UiMessageHandler {
+
+ private static final String INT_INTENT_ADD_REQUEST = "intIntentAddRequest";
+ private static final String INT_INTENT_DEL_REQUEST = "intIntentDelRequest";
+ private static final String INT_CONFIG_ADD_REQUEST = "intConfigAddRequest";
+// private static final String INT_CONFIG_DEL_REQUEST = "intConfigDelRequest";
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private IntService intService;
+
+ @Override
+ protected Collection<RequestHandler> createRequestHandlers() {
+ return ImmutableSet.of(
+ new IntIntentAddRequestHandler(),
+ new IntIntentDelRequestHandler(),
+ new IntConfigAddRequestHandler()
+// new intConfigDelRequestHandler()
+ );
+ }
+
+ private final class IntConfigAddRequestHandler extends RequestHandler {
+ private IntConfigAddRequestHandler() {
+ super(INT_CONFIG_ADD_REQUEST);
+ }
+
+ @Override
+ public void process(ObjectNode payload) {
+ log.info("intConfigAddRequest: {}", payload);
+
+ intService = get(IntService.class);
+ IntConfig.Builder builder = IntConfig.builder();
+
+ if (payload.get("collectorIp") != null) {
+ builder.withCollectorIp(IpAddress.valueOf(payload.get("collectorIp").asText()));
+ } else {
+ builder.withCollectorIp(IpAddress.valueOf("127.0.0.1"));
+ }
+
+ if (payload.get("collectorPort") != null) {
+ builder.withCollectorPort(TpPort.tpPort(
+ payload.get("collectorPort").asInt()));
+ } else {
+ builder.withCollectorPort(TpPort.tpPort(1234));
+ }
+
+ builder.enabled(true)
+ .withSinkIp(IpAddress.valueOf("10.192.19.180"))
+ .withSinkMac(MacAddress.NONE)
+ .withCollectorNextHopMac(MacAddress.BROADCAST);
+
+ intService.setConfig(builder.build());
+ }
+ }
+
+ private final class IntIntentDelRequestHandler extends RequestHandler {
+ private IntIntentDelRequestHandler() {
+ super(INT_INTENT_DEL_REQUEST);
+ }
+
+ @Override
+ public void process(ObjectNode payload) {
+ log.info("intIntentDelRequest: {}", payload);
+
+ intService = get(IntService.class);
+
+ if (payload.get("intentId") != null) {
+ intService.removeIntIntent(IntIntentId.valueOf(payload.get("intentId").asLong()));
+ }
+ }
+ }
+
+ private final class IntIntentAddRequestHandler extends RequestHandler {
+ private IntIntentAddRequestHandler() {
+ super(INT_INTENT_ADD_REQUEST);
+ }
+
+ @Override
+ public void process(ObjectNode payload) {
+ log.info("intIntentAddRequest: {}", payload);
+
+ intService = get(IntService.class);
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+ IntIntent.Builder builder = IntIntent.builder();
+
+ if (payload.get("ip4SrcPrefix") != null) {
+ sBuilder.matchIPSrc(parseIp4Prefix(payload.get("ip4SrcPrefix").asText()));
+ }
+
+ if (payload.get("ip4DstPrefix") != null) {
+ sBuilder.matchIPDst(parseIp4Prefix(payload.get("ip4DstPrefix").asText()));
+ }
+
+ if (payload.get("l4SrcPort") != null) {
+ if (payload.get("protocol") != null && payload.get("protocol").asText().equalsIgnoreCase("TCP")) {
+ sBuilder.matchTcpSrc(TpPort.tpPort(payload.get("l4SrcPort").asInt()));
+ } else {
+ sBuilder.matchUdpSrc(TpPort.tpPort(payload.get("l4SrcPort").asInt()));
+ }
+ }
+
+ if (payload.get("l4DstPort") != null) {
+ if (payload.get("protocol") != null && payload.get("protocol").asText().equalsIgnoreCase("TCP")) {
+ sBuilder.matchTcpDst(TpPort.tpPort(payload.get("l4DstPort").asInt()));
+ } else {
+ sBuilder.matchUdpDst(TpPort.tpPort(payload.get("l4DstPort").asInt()));
+ }
+ }
+
+ if (payload.get("metadata") != null) {
+ JsonNode meta = payload.get("metadata");
+ if (meta.isArray()) {
+ for (final JsonNode json : meta) {
+ switch (json.asText()) {
+ case "SWITCH_ID":
+ builder.withMetadataType(IntIntent.IntMetadataType.SWITCH_ID);
+ break;
+ case "PORT_ID":
+ builder.withMetadataType(IntIntent.IntMetadataType.L1_PORT_ID);
+ break;
+ case "HOP_LATENCY":
+ builder.withMetadataType(IntIntent.IntMetadataType.HOP_LATENCY);
+ break;
+ case "QUEUE_OCCUPANCY":
+ builder.withMetadataType(IntIntent.IntMetadataType.QUEUE_OCCUPANCY);
+ break;
+ case "INGRESS_TIMESTAMP":
+ builder.withMetadataType(IntIntent.IntMetadataType.INGRESS_TIMESTAMP);
+ break;
+ case "EGRESS_TIMESTAMP":
+ builder.withMetadataType(IntIntent.IntMetadataType.EGRESS_TIMESTAMP);
+ break;
+// case "QUEUE_CONGESTION":
+// builder.withMetadataType(IntIntent.IntMetadataType.QUEUE_CONGESTION);
+// break;
+ case "EGRESS_TX_UTIL":
+ builder.withMetadataType(IntIntent.IntMetadataType.EGRESS_TX_UTIL);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ builder.withSelector(sBuilder.build())
+ .withHeaderType(IntIntent.IntHeaderType.HOP_BY_HOP)
+ .withReportType(IntIntent.IntReportType.TRACKED_FLOW)
+ .withTelemetryMode(IntIntent.TelemetryMode.INBAND_TELEMETRY);
+ intService.installIntIntent(builder.build());
+ }
+
+ private Ip4Prefix parseIp4Prefix(String prefixString) {
+ if (prefixString == null) {
+ return null;
+ }
+ String[] splitString = prefixString.split("/");
+ Ip4Address ip4Address = Ip4Address.valueOf(splitString[0]);
+ int mask = splitString.length > 1 ? Integer.parseInt(splitString[1]) : 32;
+ return Ip4Prefix.valueOf(ip4Address, mask);
+ }
+ }
+}
diff --git a/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/package-info.java b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/package-info.java
new file mode 100644
index 0000000..d3d4849
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/java/org/onosproject/inbandtelemetry/app/ui/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Web UI component for IntService sample application.
+ */
+package org.onosproject.inbandtelemetry.app.ui;
diff --git a/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.css b/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.css
new file mode 100644
index 0000000..1cfd447
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.css
@@ -0,0 +1,108 @@
+/* css for sample app custom view */
+
+#ov-int-app-main {
+ padding: 20px;
+}
+.light #ov-int-app-main {
+ color: navy;
+}
+.dark #ov-int-app-main {
+ color: #88f;
+}
+
+#ov-int-app-main .button-panel {
+ margin: 10px;
+ width: 200px;
+}
+
+.light #ov-int-app-main .button-panel {
+ background-color: #ccf;
+}
+
+.dark #ov-int-app-main .button-panel {
+ background-color: #444;
+}
+
+#ov-int-app-main .int-app-button {
+ cursor: pointer;
+ padding: 4px;
+ text-align: center;
+}
+
+
+.light #ov-int-app-main .int-app-button {
+ color: white;
+ background-color: #99d;
+}
+.dark #ov-int-app-main .int-app-button {
+ color: black;
+ background-color: #aaa;
+}
+
+#ov-int-app-main .config-button-panel {
+ margin: 10px;
+ width: 200px;
+}
+
+.light #ov-int-app-main .config-button-panel {
+ background-color: #ccf;
+}
+
+.dark #ov-int-app-main .config-button-panel {
+ background-color: #444;
+}
+
+#ov-int-app-main .int-app-config-button {
+ cursor: pointer;
+ padding: 4px;
+ text-align: center;
+}
+
+
+.light #ov-int-app-main .int-app-config-button {
+ color: white;
+ background-color: #99d;
+}
+.dark #ov-int-app-main .int-app-config-button {
+ color: black;
+ background-color: #aaa;
+}
+/*---------------------------------------------------------------------------*/
+#ov-int-app-main h2 {
+ display: inline-block;
+}
+/* #ov-int-app-main .table-body{
+ display: inline-block;
+ overflow-y: scroll;
+ max-height:100px;
+} */
+
+/* Panel Styling */
+#ov-int-app-main-item-details-panel.floatpanel {
+ position: absolute;
+ top: 115px;
+}
+
+.light #ov-int-app-main-item-details-panel.floatpanel {
+ background-color: rgb(229, 234, 237);
+}
+.dark #ov-int-app-main-item-details-panel.floatpanel {
+ background-color: #3A4042;
+}
+
+#ov-int-app-main-item-details-panel h3 {
+ margin: 0;
+ font-size: large;
+}
+
+#ov-int-app-main-item-details-panel h4 {
+ margin: 0;
+}
+
+#ov-int-app-main-item-details-panel td {
+ padding: 5px;
+}
+#ov-int-app-main-item-details-panel td.label {
+ font-style: italic;
+ opacity: 0.8;
+}
diff --git a/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.html b/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.html
new file mode 100644
index 0000000..42717e0
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.html
@@ -0,0 +1,110 @@
+<!-- partial HTML -->
+<div id="ov-int-app-main">
+ <div class="config-panel">
+ <div>
+ Collector IP
+ <input type="text" required pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$" ng-model="collectorIp">
+
+ Collector Port
+ <input type="text" required pattern="^[0-9]{0,5}$" ng-model="collectorPort">
+ </div>
+ </div>
+ <div class="config-button-panel">
+ <div class = "int-app-config-button" ng-click="sendIntConfigString()">
+ Deploy
+ </div>
+ </div>
+ <hr>
+ <div class="input-panel">
+ <div>
+ Src Address
+ <input type="text" required pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?$" ng-model="ip4SrcPrefix">
+
+ Dst Address
+ <input type="text" required pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?$" ng-model="ip4DstPrefix">
+
+ Src Port
+ <input type="text" required pattern="^[0-9]{0,5}$" ng-model="l4SrcPort">
+
+ Dst Port
+ <input type="text" required pattern="^[0-9]{0,5}$" ng-model="l4DstPort">
+
+ Protocol
+ <select name="protocol" ng-model="protocol">
+ <option selected disabled hidden style="display: none" value=''></option>
+ <option value="TCP">TCP</option>
+ <option value="UDP">UDP</option>
+ </select>
+ </div>
+ <div>
+ <input type="checkbox" ng-model= "metaSwId">Switch Id
+ <input type="checkbox" ng-model= "metaPortId">Port Id
+ <input type="checkbox" ng-model= "metaHopLatency">Hop Latency
+ <input type="checkbox" ng-model= "metaQOccupancy">Queue Occupancy
+ <input type="checkbox" ng-model= "metaIngressTstamp">Ingress Timestamp
+ <input type="checkbox" ng-model= "metaEgressTstamp">Egress Timestamp
+ <!--<input type="checkbox" ng-model= "metaQCongestion">Queue Congestion Status-->
+ <input type="checkbox" ng-model= "metaEgressTx">Egress Port Tx Utilization
+ </div>
+ </div>
+
+ <div class="button-panel">
+ <div class = "int-app-button" ng-click="sendIntIntentString()">
+ Deploy
+ </div>
+ </div>
+ <!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+ <div class='int-app-main-intents'>
+ <div class="tabular-header">
+ <h2>Installed INT Intents ({{tableData.length}} total)</h2>
+ <div class="ctrl-btns">
+ <div class="refresh" ng-class="{active: autoRefresh}"
+ icon icon-id="refresh" icon-size="36"
+ tooltip tt-msg="autoRefreshTip"
+ ng-click="toggleRefresh()">
+ </div>
+
+ <!-- tooltip tt-msg="uninstallTip" -->
+ <div icon icon-size="42" icon-id="garbage"
+ ng-click="delIntIntent()"
+ ng-class="{active: ctrlBtnState.selection}">
+ </div>
+ </div>
+ </div>
+
+ <div class="summary-list" onos-table-resize>
+
+ <div class="table-header" onos-sortable-header>
+ <table>
+ <tr>
+ <td colId="id" sortable>ID </td>
+ <td colId="srcAddr" sortable>Src Address </td>
+ <td colId="dstAddr" sortable>Dst Address </td>
+ <td colId="srcPort" sortable>Src Port </td>
+ <td colId="dstPort" sortable>Dst Port </td>
+ <td colId="protocol" sortable>Protocol </td>
+ <td colId="metadata" sortable>Metadata </td>
+ </tr>
+ </table>
+ </div>
+
+ <div class="table-body">
+ <table>
+ <tr ng-repeat="item in tableData track by $index"
+ ng-click="selectCallback($event, item)"
+ ng-class="{selected: item.id === selId}">
+ <td>{{item.id}}</td>
+ <td>{{item.srcAddr}}</td>
+ <td>{{item.dstAddr}}</td>
+ <td>{{item.srcPort}}</td>
+ <td>{{item.dstPort}}</td>
+ <td>{{item.protocol}}</td>
+ <td>{{item.metadata}}</td>
+ </tr>
+ </table>
+ </div>
+
+ </div>
+ </div>
+</div>
+<!-- +++++++++++++++++++++ -->
diff --git a/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.js b/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.js
new file mode 100644
index 0000000..6b2b6cc
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.js
@@ -0,0 +1,194 @@
+(function() {
+ 'use strict';
+
+ // injected refs
+ var $log, $scope, $interval, $timeout, fs, wss, ks, ls;
+
+ // constants
+ var intIntentAddReq = 'intIntentAddRequest';
+ var intIntentDelReq = 'intIntentDelRequest';
+ var intConfigAddReq = 'intConfigAddRequest';
+
+ var refreshInterval = 1000;
+
+ var propOrder = ['id', 'srcAddr', 'dstAddr', 'srcPort', 'dstPort', 'insMask'];
+ var friendlyProps = ['IntIntent ID', 'Src Address', 'Dst Address', 'Src Port', 'Dst Port', 'Ins Mask'];
+
+ function sendIntConfigString() {
+ var configObjectNode = {
+ "collectorIp": $scope.collectorIp,
+ "collectorPort": $scope.collectorPort
+ };
+ wss.sendEvent(intConfigAddReq, configObjectNode);
+ }
+
+ function sendIntIntentString() {
+ var inst = [];
+ if ($scope.metaSwId) inst.push("SWITCH_ID");
+ if ($scope.metaPortId) inst.push("PORT_ID");
+ if ($scope.metaHopLatency) inst.push("HOP_LATENCY");
+ if ($scope.metaQOccupancy) inst.push("QUEUE_OCCUPANCY");
+ if ($scope.metaIngressTstamp) inst.push("INGRESS_TIMESTAMP");
+ if ($scope.metaEgressTstamp) inst.push("EGRESS_TIMESTAMP");
+ if ($scope.metaQCongestion) inst.push("QUEUE_CONGESTION");
+ if ($scope.metaEgressTx) inst.push("EGRESS_TX_UTIL");
+
+ var intentObjectNode = {
+ "ip4SrcPrefix": $scope.ip4SrcPrefix,
+ "ip4DstPrefix": $scope.ip4DstPrefix,
+ "l4SrcPort": $scope.l4SrcPort,
+ "l4DstPort": $scope.l4DstPort,
+ "protocol": $scope.protocol,
+ "metadata": inst
+ };
+ wss.sendEvent(intIntentAddReq, intentObjectNode);
+ }
+
+ function delIntIntent() {
+ if ($scope.selId) {
+ wss.sendEvent(intIntentDelReq, {
+ "intentId": $scope.selId
+ });
+ }
+ }
+
+ function intIntentBuildTable(o) {
+ var handlers = {},
+ root = o.tag,
+ req = o.tag + 'DataRequest',
+ resp = o.tag + 'DataResponse',
+ onSel = fs.isF(o.selCb),
+ onResp = fs.isF(o.respCb),
+ idKey = o.idKey || 'id',
+ oldTableData = [],
+ refreshPromise;
+
+ o.scope.tableData = [];
+ o.scope.changedData = [];
+ o.scope.sortParams = o.sortParams || {};
+ o.scope.autoRefresh = true;
+ o.scope.autoRefreshTip = 'Toggle auto refresh';
+
+ // === websocket functions --------------------
+ // response
+ function respCb(data) {
+ ls.stop();
+ o.scope.tableData = data[root];
+ o.scope.annots = data.annots;
+ onResp && onResp();
+
+ // checks if data changed for row flashing
+ if (!angular.equals(o.scope.tableData, oldTableData)) {
+ o.scope.changedData = [];
+ // only flash the row if the data already exists
+ if (oldTableData.length) {
+ angular.forEach(o.scope.tableData, function (item) {
+ if (!fs.containsObj(oldTableData, item)) {
+ o.scope.changedData.push(item);
+ }
+ });
+ }
+ angular.copy(o.scope.tableData, oldTableData);
+ }
+ }
+ handlers[resp] = respCb;
+ wss.bindHandlers(handlers);
+
+ // request
+ function sortCb(params) {
+ var p = angular.extend({}, params, o.query);
+ if (wss.isConnected()) {
+ wss.sendEvent(req, p);
+ ls.start();
+ }
+ }
+ o.scope.sortCallback = sortCb;
+
+ // === selecting a row functions ----------------
+ function selCb($event, selRow) {
+ var selId = selRow[idKey];
+ o.scope.selId = (o.scope.selId === selId) ? null : selId;
+ onSel && onSel($event, selRow);
+ }
+ o.scope.selectCallback = selCb;
+
+ // === autoRefresh functions ------------------
+ function fetchDataIfNotWaiting() {
+ if (!ls.waiting()) {
+ if (fs.debugOn('widget')) {
+ $log.debug('Refreshing ' + root + ' page');
+ }
+ sortCb(o.scope.sortParams);
+ }
+ }
+
+ function startRefresh() {
+ refreshPromise = $interval(fetchDataIfNotWaiting, refreshInterval);
+ }
+
+ function stopRefresh() {
+ if (refreshPromise) {
+ $interval.cancel(refreshPromise);
+ refreshPromise = null;
+ }
+ }
+
+ function toggleRefresh() {
+ o.scope.autoRefresh = !o.scope.autoRefresh;
+ o.scope.autoRefresh ? startRefresh() : stopRefresh();
+ }
+ o.scope.toggleRefresh = toggleRefresh;
+
+ // === Cleanup on destroyed scope -----------------
+ o.scope.$on('$destroy', function () {
+ wss.unbindHandlers(handlers);
+ stopRefresh();
+ ls.stop();
+ });
+
+ sortCb(o.scope.sortParams);
+ startRefresh();
+ }
+
+ var app1 = angular.module('ovIntApp', []);
+ app1.controller('OvIntAppCtrl',
+ ['$log', '$scope', '$interval', '$timeout', 'TableBuilderService',
+ 'FnService', 'WebSocketService', 'KeyService', 'LoadingService',
+
+ function(_$log_, _$scope_, _$interval_, _$timeout_, tbs, _fs_, _wss_, _ks_, _ls_) {
+ $log = _$log_;
+ $scope = _$scope_;
+ $interval = _$interval_;
+ $timeout = _$timeout_;
+ fs = _fs_;
+ wss = _wss_;
+ ks = _ks_;
+ ls = _ls_;
+
+ // custom selection callback
+ function selCb($event, row) {
+ }
+ intIntentBuildTable({
+ scope: $scope,
+ tag: 'intAppIntIntent'
+ // selCb: selCb
+ });
+
+ $scope.sendIntIntentString = sendIntIntentString;
+ $scope.delIntIntent = delIntIntent;
+ $scope.sendIntConfigString = sendIntConfigString;
+
+ // get data the first time...
+ // getData();
+
+ // cleanup
+ $scope.$on('$destroy', function() {
+ // wss.unbindHandlers(handlers);
+ /*ks.unbindKeys();*/
+ $log.log('OvIntAppCtrl has been destroyed');
+ });
+
+ $log.log('OvIntAppCtrl has been created');
+ }
+ ]);
+}());
diff --git a/apps/inbandtelemetry/app/src/main/resources/intApp/css.html b/apps/inbandtelemetry/app/src/main/resources/intApp/css.html
new file mode 100644
index 0000000..4996aa6
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/resources/intApp/css.html
@@ -0,0 +1 @@
+<link rel="stylesheet" href="app/view/intApp/intApp.css">
diff --git a/apps/inbandtelemetry/app/src/main/resources/intApp/js.html b/apps/inbandtelemetry/app/src/main/resources/intApp/js.html
new file mode 100644
index 0000000..9a7b0ac
--- /dev/null
+++ b/apps/inbandtelemetry/app/src/main/resources/intApp/js.html
@@ -0,0 +1 @@
+<script src="app/view/intApp/intApp.js"></script>
diff --git a/apps/inbandtelemetry/impl/BUCK b/apps/inbandtelemetry/impl/BUCK
new file mode 100644
index 0000000..9c87c92
--- /dev/null
+++ b/apps/inbandtelemetry/impl/BUCK
@@ -0,0 +1,28 @@
+BUNDLES = [
+ '//apps/inbandtelemetry/api:onos-apps-inbandtelemetry-api',
+ ':onos-apps-inbandtelemetry-impl'
+]
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//lib:KRYO',
+ '//core/store/serializers:onos-core-serializers',
+ '//pipelines/basic:onos-pipelines-basic',
+ '//apps/inbandtelemetry/api:onos-apps-inbandtelemetry-api',
+]
+
+TEST_DEPS = [
+ '//lib:TEST_ADAPTERS',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ test_deps = TEST_DEPS,
+)
+
+onos_app(
+ title = 'P4 In-band Network Telemetry Service',
+ description = 'Provides managements of INT-capable devices. Specifies flows to enable INT and' +
+ 'types of metadata to collect. Sets up INT-related information.',
+ category = 'Monitoring',
+ included_bundles = BUNDLES,
+)
\ No newline at end of file
diff --git a/apps/inbandtelemetry/impl/pom.xml b/apps/inbandtelemetry/impl/pom.xml
new file mode 100644
index 0000000..455d12e
--- /dev/null
+++ b/apps/inbandtelemetry/impl/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016-present Open Networking Foundation
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>onos-apps-inbandtelemetry</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.14.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-apps-inbandtelemetry-impl</artifactId>
+ <packaging>bundle</packaging>
+
+ <url>http://onosproject.org</url>
+
+ <description>P4 INT Management Implementation</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-apps-inbandtelemetry-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-core-serializers</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/IntManager.java b/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/IntManager.java
new file mode 100644
index 0000000..5cf32da
--- /dev/null
+++ b/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/IntManager.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.inbandtelemetry.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.inbandtelemetry.api.IntConfig;
+import org.onosproject.inbandtelemetry.api.IntIntent;
+import org.onosproject.inbandtelemetry.api.IntIntentId;
+import org.onosproject.inbandtelemetry.api.IntObjective;
+import org.onosproject.inbandtelemetry.api.IntProgrammable;
+import org.onosproject.inbandtelemetry.api.IntService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.AtomicIdGenerator;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.slf4j.Logger;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of IntService, for controlling INT-capable pipelines.
+ */
+@Component(immediate = true)
+@Service
+public class IntManager implements IntService {
+ private final String appName = "org.onosproject.inbandtelemetry";
+ private ApplicationId appId;
+ private final Logger log = getLogger(getClass());
+ private ConsistentMap<IntIntentId, IntIntent> intentConsistentMap;
+ private ConsistentMap<DeviceId, IntDeviceRole> deviceRoleConsistentMap;
+ private IntConfig cfg;
+ private AtomicIdGenerator intentIds;
+
+ private InternalHostListener hostListener = new InternalHostListener();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private FlowRuleService flowRuleService;
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication(appName);
+
+ KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(IntIntent.class)
+ .register(IntIntentId.class)
+ .register(IntDeviceRole.class)
+ .register(IntIntent.IntHeaderType.class)
+ .register(IntIntent.IntMetadataType.class)
+ .register(IntIntent.IntReportType.class)
+ .register(IntIntent.TelemetryMode.class);
+
+ intentConsistentMap = storageService.<IntIntentId, IntIntent>consistentMapBuilder()
+ .withSerializer(Serializer.using(serializer.build()))
+ .withName("int-intents")
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .build();
+
+ deviceRoleConsistentMap = storageService.<DeviceId, IntDeviceRole>consistentMapBuilder()
+ .withSerializer(Serializer.using(serializer.build()))
+ .withName("int-device-roles")
+ .withApplicationId(appId)
+ .withPurgeOnUninstall()
+ .build();
+
+ // Assign IntDeviceRole to each device
+ deviceService.getAvailableDevices().forEach(device ->
+ deviceRoleConsistentMap.put(device.id(),
+ hostService.getConnectedHosts(device.id()).isEmpty() ?
+ IntDeviceRole.TRANSIT :
+ IntDeviceRole.SOURCE_SINK)
+ );
+ hostService.addListener(hostListener);
+ intentIds = storageService.getAtomicIdGenerator("int-intent-id-generator");
+ startInt();
+ log.info("Started", appId.id());
+ }
+
+ @Deactivate
+ public void deactivate() {
+ hostService.removeListener(hostListener);
+ log.info("Deactivated");
+ }
+
+ @Override
+ public void startInt() {
+ deviceService.getAvailableDevices().forEach(device -> {
+ if (device.is(IntProgrammable.class)) {
+ IntProgrammable intDevice = device.as(IntProgrammable.class);
+ intDevice.init();
+ }
+ });
+ }
+
+ @Override
+ public void startInt(Set<DeviceId> deviceIds) {
+ deviceIds.forEach(deviceId -> {
+ Device device = deviceService.getDevice(deviceId);
+ if (device.is(IntProgrammable.class) &&
+ getIntRole(deviceId) == IntDeviceRole.TRANSIT) {
+ IntProgrammable intDevice = device.as(IntProgrammable.class);
+ intDevice.init();
+ }
+ });
+ }
+
+ @Override
+ public void stopInt() {
+ flowRuleService.removeFlowRulesById(appId);
+ }
+
+ @Override
+ public void stopInt(Set<DeviceId> deviceIds) {
+
+ }
+
+ @Override
+ public void setConfig(IntConfig cfg) {
+ this.cfg = cfg;
+ deviceService.getAvailableDevices().forEach(device -> {
+ if (device.is(IntProgrammable.class)) {
+ IntProgrammable intDevice = device.as(IntProgrammable.class);
+ intDevice.setupIntConfig(cfg);
+ }
+ });
+ }
+
+ @Override
+ public IntConfig getConfig() {
+ return cfg;
+ }
+
+ @Override
+ public IntIntentId installIntIntent(IntIntent intent) {
+ Integer intentId = (int) intentIds.nextId();
+ IntIntentId intIntentId = IntIntentId.valueOf(intentId);
+ intentConsistentMap.put(intIntentId, intent);
+
+ // Convert IntIntent into an IntObjective
+ IntObjective obj = new IntObjective.Builder()
+ .withSelector(intent.selector())
+ .withMetadataTypes(intent.metadataTypes())
+ .withHeaderType(intent.headerType())
+ .build();
+
+ // Install IntObjective on each INT source device
+ deviceService.getAvailableDevices().forEach(device -> {
+ if (device.is(IntProgrammable.class)
+ && deviceRoleConsistentMap.get(device.id()).value() == IntDeviceRole.SOURCE_SINK) {
+ IntProgrammable intDevice = device.as(IntProgrammable.class);
+ intDevice.addIntObjective(obj);
+ }
+ });
+ return intIntentId;
+ }
+
+ @Override
+ public void removeIntIntent(IntIntentId intentId) {
+ IntIntent intent = intentConsistentMap.remove(intentId).value();
+
+ // Convert IntIntent into an IntObjective
+ IntObjective obj = new IntObjective.Builder()
+ .withSelector(intent.selector())
+ .withMetadataTypes(intent.metadataTypes())
+ .withHeaderType(intent.headerType())
+ .build();
+
+ // Remove IntObjective on each INT source device
+ deviceService.getAvailableDevices().forEach(device -> {
+ if (device.is(IntProgrammable.class)
+ && deviceRoleConsistentMap.get(device.id()).value() == IntDeviceRole.SOURCE_SINK) {
+ IntProgrammable intDevice = device.as(IntProgrammable.class);
+ intDevice.removeIntObjective(obj);
+ }
+ });
+ }
+
+ @Override
+ public IntIntent getIntIntent(IntIntentId intentId) {
+ return Optional.ofNullable(intentConsistentMap.get(intentId).value()).orElse(null);
+ }
+
+ @Override
+ public Map<IntIntentId, IntIntent> getIntIntents() {
+ return intentConsistentMap.asJavaMap();
+ }
+
+ private IntDeviceRole getIntRole(DeviceId deviceId) {
+ return deviceRoleConsistentMap.get(deviceId).value();
+ }
+
+ private void setIntRole(DeviceId deviceId, IntDeviceRole role) {
+ deviceRoleConsistentMap.put(deviceId, role);
+ }
+
+ private class InternalHostListener implements HostListener {
+ @Override
+ public void event(HostEvent event) {
+ DeviceId deviceId = event.subject().location().deviceId();
+ if (!deviceService.getDevice(deviceId).is(IntProgrammable.class)) {
+ return;
+ }
+ switch (event.type()) {
+ case HOST_ADDED:
+ // When a host is attached to the switch, we can configure it
+ // to work as SOURCE_SINK switch.
+ if (deviceRoleConsistentMap.getOrDefault(deviceId, IntDeviceRole.TRANSIT).value()
+ != IntDeviceRole.SOURCE_SINK) {
+ setIntRole(deviceId, IntDeviceRole.SOURCE_SINK);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+}
diff --git a/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/package-info.java b/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/package-info.java
new file mode 100644
index 0000000..d045fe9
--- /dev/null
+++ b/apps/inbandtelemetry/impl/src/main/java/org/onosproject/inbandtelemetry/impl/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Service to control a network of devices capable of collecting and exporting
+ * data plane telemetry via in-band mechanism.
+ */
+package org.onosproject.inbandtelemetry.impl;
diff --git a/apps/inbandtelemetry/pom.xml b/apps/inbandtelemetry/pom.xml
index 57a8351..0105fbe 100644
--- a/apps/inbandtelemetry/pom.xml
+++ b/apps/inbandtelemetry/pom.xml
@@ -32,5 +32,6 @@
<modules>
<module>api</module>
+ <module>impl</module>
</modules>
</project>