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/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>