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/int-gui2-lib/projects/int-gui2-lib/src/lib/intapp/intapp.component.css b/apps/inbandtelemetry/int-gui2-lib/projects/int-gui2-lib/src/lib/intapp/intapp.component.css
new file mode 100644
index 0000000..c57601b
--- /dev/null
+++ b/apps/inbandtelemetry/int-gui2-lib/projects/int-gui2-lib/src/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/int-gui2-lib/projects/int-gui2-lib/src/lib/intapp/intapp.component.html b/apps/inbandtelemetry/int-gui2-lib/projects/int-gui2-lib/src/lib/intapp/intapp.component.html
new file mode 100644
index 0000000..c1fe049
--- /dev/null
+++ b/apps/inbandtelemetry/int-gui2-lib/projects/int-gui2-lib/src/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/int-gui2-lib/projects/int-gui2-lib/src/lib/intapp/intapp.component.ts b/apps/inbandtelemetry/int-gui2-lib/projects/int-gui2-lib/src/lib/intapp/intapp.component.ts
new file mode 100644
index 0000000..c5ccf50
--- /dev/null
+++ b/apps/inbandtelemetry/int-gui2-lib/projects/int-gui2-lib/src/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 'gui2-fw-lib';
+
+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',
+ '../../../fw/widget/table.css',
+ '../../../fw/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});
+ }
+ }
+}