INT app gui2
This patch contains the files needed by the INT app to work with gui2.
More in detail I reuse/modifie the html and the css, already provided in
the inbandtelemetry folder. I add the file needed to utilize the gui2,
e.g,intapp-gui2-lib.module.ts.
Link to my dropbox for screenshots:
https://www.dropbox.com/sh/ie2l0flcm8igesk/AAAsP2RdMbNbnt6A88lmBczaa?dl=0
Change-Id: I710c09f3a04a139a4482bb7aae3b3b76b39e67e6
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
index fafd4b4..b7380a6 100644
--- 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
@@ -40,6 +40,7 @@
*/
public class IntAppTableMessageHandler extends UiMessageHandler {
private static final String INT_APP_INT_INTENT = "intAppIntIntent";
+ private static final String INT_APP_INT_INTENT_PAYLOAD = "intAppIntIntents";
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";
@@ -73,7 +74,7 @@
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);
+ super(INT_APP_INT_INTENT_DATA_REQUEST, INT_APP_INT_INTENT_DATA_RESPONSE, INT_APP_INT_INTENT_PAYLOAD);
}
@Override
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
index 9769bb0..daa1968 100644
--- 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
@@ -61,6 +61,7 @@
new UiExtension.Builder(getClass().getClassLoader(), uiViews)
.resourcePath(VIEW_ID)
.messageHandlerFactory(messageHandlerFactory)
+ .ui2()
.build();
@Activate
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
index 0dc18a4..0dcf9d0 100644
--- 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
@@ -36,6 +36,8 @@
import java.util.Collection;
+import static org.onosproject.inbandtelemetry.api.IntIntent.IntHeaderType.HOP_BY_HOP;
+
public class IntAppUiMessageHandler extends UiMessageHandler {
private static final String INT_INTENT_ADD_REQUEST = "intIntentAddRequest";
@@ -53,7 +55,7 @@
new IntIntentAddRequestHandler(),
new IntIntentDelRequestHandler(),
new IntConfigAddRequestHandler()
-// new intConfigDelRequestHandler()
+ //new intConfigDelRequestHandler()
);
}
@@ -180,7 +182,7 @@
}
builder.withSelector(sBuilder.build())
- .withHeaderType(IntIntent.IntHeaderType.HOP_BY_HOP)
+ .withHeaderType(HOP_BY_HOP)
.withReportType(IntIntent.IntReportType.TRACKED_FLOW)
.withTelemetryMode(IntIntent.TelemetryMode.INBAND_TELEMETRY);
intService.installIntIntent(builder.build());
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
index e552001..3d4f652 100644
--- a/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.js
+++ b/apps/inbandtelemetry/app/src/main/resources/app/view/intApp/intApp.js
@@ -53,7 +53,7 @@
function intIntentBuildTable(o) {
var handlers = {},
- root = o.tag,
+ root = o.tag + 's',
req = o.tag + 'DataRequest',
resp = o.tag + 'DataResponse',
onSel = fs.isF(o.selCb),
diff --git a/apps/inbandtelemetry/intApp-gui2/intApp/BUILD.bazel b/apps/inbandtelemetry/intApp-gui2/intApp/BUILD.bazel
new file mode 100644
index 0000000..aecef14
--- /dev/null
+++ b/apps/inbandtelemetry/intApp-gui2/intApp/BUILD.bazel
@@ -0,0 +1,46 @@
+"""
+ Copyright 2020-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(default_visibility = ["//:__subpackages__"])
+
+load("@npm_angular_bazel//:index.bzl", "ng_module")
+
+ng_module(
+ name = "intapp-gui2-lib",
+ srcs = glob(
+ include = [
+ "**/*.ts",
+ ],
+ exclude = [
+ "**/*.spec.ts",
+ ],
+ ),
+ assets = glob([
+ "**/*.css",
+ "**/*.html",
+ ]),
+ tsconfig = "//web/gui2:tsconfig.json",
+ deps = [
+ "//web/gui2-fw-lib",
+ "@npm//@angular/animations",
+ "@npm//@angular/core",
+ "@npm//@angular/forms",
+ "@npm//@angular/platform-browser-dynamic",
+ "@npm//@angular/router",
+ "@npm//@types",
+ "@npm//rxjs",
+ ],
+)
diff --git a/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp-gui2-lib.module.ts b/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp-gui2-lib.module.ts
new file mode 100644
index 0000000..9dae779
--- /dev/null
+++ b/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp-gui2-lib.module.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020-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.
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import {RouterModule} from '@angular/router';
+import { Gui2FwLibModule } from 'org_onosproject_onos/web/gui2-fw-lib/public_api';
+import { IntAppComponent } from './intapp/intapp.component';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+@NgModule({
+ declarations: [
+ IntAppComponent,
+ ],
+ imports: [
+ CommonModule,
+ RouterModule.forChild([{path: '', component: IntAppComponent}]),
+ Gui2FwLibModule,
+ FormsModule,
+ ReactiveFormsModule
+ ],
+ exports: [
+ IntAppComponent,
+ ]
+})
+export class intAppGui2LibModule { }
diff --git a/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp/intapp.component.css b/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp/intapp.component.css
new file mode 100644
index 0000000..c57601b
--- /dev/null
+++ b/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp/intapp.component.css
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2020-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.
+ */
+
+/*
+ ONOS GUI -- INT App View (theme) -- CSS file
+ */
+
+#ov-int-app-main {
+ padding: 5px;
+}
+
+#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;
+}
+
+.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;
+}
+
+#ov-int-app-main input {
+ padding: 4px;
+ font-size: inherit;
+}
+
+.dark #ov-int-app-main .int-app-config-button {
+ color: black;
+ background-color: #aaa;
+}
+/*---------------------------------------------------------------------------*/
+#ov-int-app-main hr {
+ border: 0;
+ height: 1px;
+ background: #333;
+}
+
+#ov-int-app-main h2 {
+ display: inline-block;
+ margin: 10px 0px 10px;
+}
+
+#ov-int-app-main h3 {
+ display: inline-block;
+ margin-bottom: 5px;
+}
+
+#ov-int-app-main h4 {
+ display: inline-block;
+ margin-bottom: 5px;
+}
+
+
+/* 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 h4 {
+ margin: 0;
+}
+
+#ov-int-app-main-item-details-panel h3 {
+ margin: 0;
+ font-size: medium;
+}
+#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;
+}
+
+.error-text{
+ color:red;
+}
+
+.myrow {
+ display: flex;
+ flex-wrap: wrap;
+}
+.mygrid {
+ flex: 1;
+ min-width: 25%;
+ padding : 10px;
+}
+
+/* Table Css*/
+
+#int-app-main-intents.tabular-header {
+ text-align: left;
+}
+#int-app-main-intents div.summary-list .table-header td {
+ font-weight: bold;
+ font-variant: small-caps;
+ text-transform: uppercase;
+ font-size: 10pt;
+ padding-top: 8px;
+ padding-bottom: 8px;
+ letter-spacing: 0.02em;
+ cursor: pointer;
+ background-color: #e5e5e6;
+ color: #3c3a3a;
+}
+
+#int-app-main-intents div.summary-list .table-body {
+ overflow:scroll;
+}
+
+#ov-int-app-main hr {
+ border: 0;
+ height: 1px;
+ background: #333;
+}
+
+#ov-int-app-main h2 {
+ display: inline-block;
+ margin: 15px 0px 15px;
+}
+
+#ov-int-app-main h3 {
+ display: inline-block;
+ margin-bottom: 10px;
+}
+
+#ov-int-app-main h4 {
+ display: inline-block;
+ margin-bottom: 10px;
+}
+
+.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;
+}
+
+#int-app-main-intents, div.ctrl-btns {
+}
+
+#int-app-main-intents th, td {
+ text-align: left;
+ padding: 8px;
+}
+
+
diff --git a/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp/intapp.component.html b/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp/intapp.component.html
new file mode 100644
index 0000000..c1fe049
--- /dev/null
+++ b/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp/intapp.component.html
@@ -0,0 +1,195 @@
+<!--
+~ Copyright 2020-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.
+-->
+<div id="ov-int-app-main">
+ <div class="tabular-header">
+ <h2 style="font-weight: bold">In-band Network Telemetry (INT) Control
+ Application</h2>
+ </div>
+
+ <hr>
+
+ <div class="tabular-header">
+ <h2>
+ INT Collector Configuration
+ </h2>
+ <div class="config-panel">
+ <h3>
+ Collector IPv4 address and UDP port
+ </h3>
+ <form [formGroup]="formConf" (ngSubmit)="sendIntConfigString()">
+ <input type="text" placeholder="IPv4 address" [ngClass]="{ 'is-invalid': formConf.controls.colIp.errors }"
+ class="form-control" formControlName="colIp">
+ :
+ <input type="text" placeholder="port" class="form-control" [ngClass]="{ 'is-invalid': formConf.controls.colPort.errors }" formControlName="colPort">
+ <div *ngIf="formConf.controls.colIp.errors" class="invalid-feedback">
+ <div class="error-text" *ngIf="formConf.controls.colIp.errors.required"> Control IP is required</div>
+ <div class="error-text" *ngIf="formConf.controls.colIp.errors.pattern">Control IP must be a valid IP address</div>
+ </div>
+ <div *ngIf="formConf.controls.colPort.errors" class="invalid-feedback">
+ <div class="error-text" *ngIf="formConf.controls.colPort.errors.required">Port is required</div>
+ <div class="error-text" *ngIf="formConf.controls.colPort.errors.pattern">Port must be a valid Port number</div>
+ </div>
+ <div class="config-button-panel">
+ <button class="int-app-button">
+ Apply Configuration
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+ <div class="tabular-header">
+ <h2 style="font-weight: bold">In-band Network Telemetry (INT) Control
+ Application</h2>
+ </div>
+
+ <hr>
+ <div class="tabular-header">
+ <h2>
+ INT Watchlist Rules
+ </h2>
+ <div>
+ <form [formGroup]="formSend" (ngSubmit)="sendIntIntentString()">
+ <div class="input-panel">
+ <h3>
+ Create New Watchlist Rule
+ </h3>
+ <div>
+ <label>
+ <input type="text" placeholder="Source IP address" [ngClass]="{ 'is-invalid': formSend.controls.ip4SrcPrefix.errors }"
+ class="form-control" formControlName="ip4SrcPrefix">
+
+ </label>
+ <label>
+ <input type="text" placeholder="Dest IP address" [ngClass]="{ 'is-invalid': formSend.controls.ip4DstPrefix.errors }"
+ class="form-control" formControlName="ip4DstPrefix">
+ </label>
+ <label>
+ <input type="text" placeholder="Source port" [ngClass]="{ 'is-invalid': formSend.controls.l4SrcPort.errors }"
+ class="form-control" formControlName="l4SrcPort">
+ </label>
+ <label>
+ <input type="text" placeholder="Dest port" [ngClass]="{ 'is-invalid': formSend.controls.l4DstPort.errors }"
+ class="form-control" formControlName="l4DstPort">
+ </label>
+ <label>
+ Protocol
+ <select name="protocol" formControlName="protocol">
+ <option selected disabled hidden
+ style="display: none" value=''></option>
+ <option value="TCP">TCP</option>
+ <option value="UDP">UDP</option>
+ </select>
+ </label>
+ <div *ngIf="formSend.controls.ip4DstPrefix.errors" class="invalid-feedback">
+ <div class="error-text" *ngIf="formSend.controls.ip4DstPrefix.errors.required"> Destination IP is required</div>
+ <div class="error-text" *ngIf="formSend.controls.ip4DstPrefix.errors.pattern">Destination IP must be a valid IP address</div>
+ </div>
+ <div *ngIf="formSend.controls.ip4SrcPrefix.errors" class="invalid-feedback">
+ <div class="error-text" *ngIf="formSend.controls.ip4SrcPrefix.errors.required">Source IP is required</div>
+ <div class="error-text" *ngIf="formSend.controls.ip4SrcPrefix.errors.pattern">Source IP must be a valid IP address</div>
+ </div>
+ <div *ngIf="formSend.controls.l4DstPort.errors" class="invalid-feedback">
+ <div class="error-text" *ngIf="formSend.controls.l4DstPort.errors.pattern">Destination Port must be a valid Port</div>
+ </div>
+ <div *ngIf="formSend.controls.l4SrcPort.errors" class="invalid-feedback">
+ <div class="error-text" *ngIf="formSend.controls.l4SrcPort.errors.pattern">Source Port must be a valid Port</div>
+ </div>
+ </div>
+ <div>
+ <div class="myrow">
+ <div *ngFor="let data of metaData">
+ <div class="mygrid">
+ <label>
+ <input type="checkbox"
+ (change)="onCheckboxChange(data.value, $event.target.checked)"/>
+ {{ data.name }}
+ </label>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="button-panel">
+ <button class="int-app-button">
+ Apply Watchlist Rule
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+
+ <!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+ <div class='int-app-main-intents'>
+ <div class="tabular-header">
+ <h2>Installed Watchlist Rules ({{tableData.length}} total)</h2>
+ <div class="ctrl-btns">
+ <div class="refresh" (click)="toggleRefresh()">
+ <onos-icon classes="{{ autoRefresh?'active refresh':'refresh'}}" iconId="refresh" iconSize="42"
+ toolTip="{{ autoRefreshTip }}"></onos-icon>
+ </div>
+ <!-- tooltip tt-msg="uninstallTip" -->
+ <div (click)="delIntIntent()">
+ <onos-icon classes="{active: ctrlBtnState.selection}" iconId="garbage"
+ iconSize="42"></onos-icon>
+ </div>
+ </div>
+ </div>
+
+ <div id="summary-list" class="summary-list" onosTableResize>
+ <div class="table-header">
+ <table>
+ <tr>
+ <td colId="available" class="table-icon"></td>
+ <td colId="type" class="table-icon"></td>
+ <td colId="id">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 class="table-body" *ngIf="tableData.length === 0" class="no-data">
+ <td colspan="9">{{ annots.noRowsMsg }}</td>
+ </tr>
+ <tr *ngFor="let row of tableData | filter : tableDataFilter"
+ (click)="selectCallback($event, row)"
+ [ngClass]="{selected: row.id === selId, 'data-change': isChanged(row.id)}">
+ <td class="table-icon">
+ <onos-icon classes="{{ row._iconid_available}}"
+ iconId={{row._iconid_available}}></onos-icon>
+ </td>
+ <td class="table-icon">
+ <onos-icon classes="{{row._iconid_type? 'active-type':undefined}}"
+ iconId="{{row._iconid_type}}"></onos-icon>
+ </td>
+ <td>{{ row.id }}</td>
+ <td>{{ row.srcAddr }}</td>
+ <td>{{ row.dstAddr}}</td>
+ <td>{{ row.srcPort}}</td>
+ <td>{{ row.dstPort }}</td>
+ <td>{{ row.protocol }}</td>
+ <td>{{ row.metadata }}</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp/intapp.component.ts b/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp/intapp.component.ts
new file mode 100644
index 0000000..ea2a328
--- /dev/null
+++ b/apps/inbandtelemetry/intApp-gui2/intApp/lib/intapp/intapp.component.ts
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2020-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.
+ */
+import {Component, OnInit, OnDestroy} from '@angular/core';
+import { FormBuilder, FormGroup, FormArray, FormControl, Validators } from '@angular/forms';
+
+import {
+ FnService,
+ LogService,
+ WebSocketService,
+ SortDir, TableBaseImpl, TableResponse,
+} from 'org_onosproject_onos/web/gui2-fw-lib/public_api';
+
+import {ActivatedRoute, Router} from '@angular/router';
+
+// constants
+const intIntentAddReq = 'intIntentAddRequest';
+const intIntentDelReq = 'intIntentDelRequest';
+const intConfigAddReq = 'intConfigAddRequest';
+const regColIp = '^([0-9]{1,3}\\.){3}[0-9]{1,3}$';
+const regColPort = '^[0-9]{0,5}$';
+const regSendIp = '^([0-9]{1,3}\\.){3}[0-9]{1,3}(/[0-9]{1,2})?$'
+const regSendPort ='^[0-9]{0,5}$'
+
+export interface Metadata {
+ name: string;
+ value: string;
+}
+
+/**
+ * ONOS GUI -- INT App View Component
+ */
+@Component({
+ selector: 'int-app',
+ templateUrl: './intapp.component.html',
+ styleUrls: ['./intapp.component.css',
+ '../../../../../../web/gui2-fw-lib/lib/widget/table.css',
+ '../../../../../../web/gui2-fw-lib/lib/widget/table.theme.css'
+ ]
+})
+export class IntAppComponent extends TableBaseImpl implements OnInit, OnDestroy {
+
+ formConf: FormGroup;
+ formSend: FormGroup;
+ metaData: Metadata[] = [
+ { name: 'Switch ID', value : 'SWITCH_ID'},
+ { name: 'Port IDs', value:'PORT_ID' },
+ { name: 'Hop Latency', value: 'HOP_LATENCY', },
+ { name: 'Queue Occupancy', value:'QUEUE_OCCUPANCY' },
+ { name: 'Ingress Timestamp', value:'INGRESS_TIMESTAMP' },
+ { name: 'Egress Timestamp', value: 'EGRESS_TIMESTAMP' },
+ { name: 'Egress Port Tx Utilization', value:'EGRESS_TX_UTIL' },
+ ];
+
+ constructor(
+ protected fs: FnService,
+ protected log: LogService,
+ protected as: ActivatedRoute,
+ protected router: Router,
+ protected wss: WebSocketService,
+ protected fb: FormBuilder,
+ ) {
+ super(fs, log, wss, 'intAppIntIntent');
+
+ }
+
+ ngOnInit() {
+ this.init();
+ this.formSend = this.fb.group({
+ name: this.fb.array([]),
+ ip4SrcPrefix: new FormControl(null, [Validators.required, Validators.pattern(regSendIp)]),
+ ip4DstPrefix: new FormControl(null, [Validators.required, Validators.pattern(regSendIp)]),
+ l4SrcPort: new FormControl(null, [ Validators.pattern(regSendPort)]),
+ l4DstPort: new FormControl(null, [ Validators.pattern(regSendPort)]),
+ protocol: new FormControl(),
+ });
+ this.formConf = this.fb.group({
+ colIp: new FormControl(null, [Validators.required, Validators.pattern(regColIp)]),
+ colPort: new FormControl( null, [Validators.required, Validators.pattern(regColPort)])
+ });
+ this.log.debug('IntAppComponent initialized');
+ }
+
+ ngOnDestroy() {
+ this.destroy();
+ this.log.debug('IntAppComponent destroyed');
+ }
+
+ navto(path) {
+ this.log.debug('navigate');
+ if (this.selId) {
+ this.router.navigate([path], {queryParams: {itemId: this.selId}});
+ }
+ }
+
+ onCheckboxChange(name: string, isChecked: boolean) {
+ this.log.debug('event'+ isChecked);
+ const meta = (this.formSend.controls.name as FormArray);
+
+ if (isChecked) {
+ meta.push(new FormControl(name));
+ } else {
+ const index = meta.controls.findIndex(x => x.value === name);
+ meta.removeAt(index);
+ }
+ }
+
+ sendIntConfigString() {
+ if (this.formConf.invalid) {
+ return;
+ }
+ let configObjectNode = {
+ "collectorIp": this.formConf.value.colIp,
+ "collectorPort": this.formConf.value.colPort
+ };
+ this.wss.sendEvent(intConfigAddReq, configObjectNode);
+ }
+
+ sendIntIntentString() {
+ if (this.formSend.invalid) {
+ return;
+ }
+ let intentObjectNode = {
+ "ip4SrcPrefix": this.formSend.value.ip4SrcPrefix,
+ "ip4DstPrefix": this.formSend.value.ip4DstPrefix,
+ "l4SrcPort": this.formSend.value.l4SrcPort,
+ "l4DstPort": this.formSend.value.l4DstPort,
+ "protocol": this.formSend.value.protocol,
+ "metadata": this.formSend.value.name
+ };
+ this.wss.sendEvent(intIntentAddReq, intentObjectNode);
+ }
+
+ delIntIntent(){
+ if (this.selId) {
+ this.wss.sendEvent(intIntentDelReq,{"intentId": this.selId});
+ }
+ }
+}
diff --git a/apps/inbandtelemetry/intApp-gui2/intApp/public_api.ts b/apps/inbandtelemetry/intApp-gui2/intApp/public_api.ts
new file mode 100644
index 0000000..7b8fc29
--- /dev/null
+++ b/apps/inbandtelemetry/intApp-gui2/intApp/public_api.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019-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.
+ */
+
+/*
+ * Public API Surface of intApp-gui2-lib
+ */
+
+export * from './lib/intapp-gui2-lib.module';
+export * from './lib/intapp/intapp.component';
diff --git a/apps/inbandtelemetry/intApp-gui2/intApp/test.ts b/apps/inbandtelemetry/intApp-gui2/intApp/test.ts
new file mode 100644
index 0000000..3e4cb26
--- /dev/null
+++ b/apps/inbandtelemetry/intApp-gui2/intApp/test.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019-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.
+ */
+
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'core-js/es7/reflect';
+import 'zone.js/dist/zone';
+import 'zone.js/dist/zone-testing';
+import { getTestBed } from '@angular/core/testing';
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+declare const require: any;
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);