[ONOS-7731] Update api interface & implementation of openstack vtap app
Change-Id: I7c3c7888b00a7357b13e3b1756e9cd0a1bb6a5c0
diff --git a/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index c3fa886..7fd8ccf 100644
--- a/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -42,7 +42,7 @@
</command>
<command>
- <action class="org.onosproject.openstackvtap.cli.OpenstackVtapOutputCommand" />
+ <action class="org.onosproject.openstackvtap.cli.OpenstackVtapNetworkListCommand" />
</command>
</command-bundle>
diff --git a/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.css b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.css
new file mode 100644
index 0000000..2595410
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.css
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018-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 -- OpenstackVtap -- CSS file
+ */
+
+/* --- Topo Vtap Panel --- */
+#topo-p-vtap {
+ padding: 16px;
+ top: 190px;
+ left: 20px;
+}
+
+.topo-p td input.number-input {
+ width: 50px;
+}
+
+.topo-p div.footer tbody {
+ float: right;
+}
+
+.topo-p div.footer tbody input.button-input {
+ background-color: #5b99d2;
+ border: 0;
+ border-radius: 3px;
+ color: #fff;
+ height: 25px;
+ width: 60px;
+ padding: 1px 0 0 0;
+}
+
+#vtap-dialog {
+ padding: 16px;
+ top: 190px;
+ left: 20px;
+}
diff --git a/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.html b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.html
new file mode 100644
index 0000000..f1a1b72
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.html
@@ -0,0 +1,4 @@
+<!-- partial HTML -->
+<div id="ov-openstackvtap">
+ <p>This is a hidden view .. just a placeholder to house the javascript</p>
+</div>
diff --git a/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.js b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.js
new file mode 100644
index 0000000..9b20eb0
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.js
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2018-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 -- Openstack Vtap View Module
+ */
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, tov, ovts, fs, wss, tts, api, gs, ps, ds;
+
+ // constants
+ var pCls = 'topo-p',
+ idVta = 'topo-p-vtap',
+ idVDi = 'vtap-dialog',
+ vtapPanelOpts = {
+ edge: 'left',
+ width: 330, // vtap panel width
+ }
+
+ // panels
+ var vtap;
+
+ // selected info
+ var selectedItem;
+
+ // constants
+ var osvIsActReq = 'openstackVtapIsActivatedRequest',
+ osvIsActResp = 'openstackVtapIsActivatedResponse',
+ osvCreateReq = 'openstackVtapCreateRequest',
+ osvCreateResp = 'openstackVtapCreateResponse';
+
+ // function to be replaced by the localization bundle function
+ var topoLion = function (x) {
+ return '#ttrafov#' + x + '#';
+ };
+
+ var overlay = {
+ overlayId: 'vtap-overlay',
+ glyphId: 'm_details',
+ tooltip: 'Openstack Vtap Overlay',
+
+ activate: function () {
+ $log.debug("Openstack Vtap topology overlay ACTIVATED");
+ },
+ deactivate: function () {
+ destroyVtapPanel();
+ $log.debug("Openstack Vtap topology overlay DEACTIVATED");
+ },
+
+ // detail panel button definitions
+ buttons: {
+ createHostVtapBtn: {
+ gid: 'm_details',
+ tt: 'Create Host-to-Host Vtap',
+ cb: displayVtap
+ },
+ showRelatedTraffic: {
+ gid: 'm_relatedIntents',
+ tt: function () { return topoLion('tr_btn_show_related_traffic'); },
+ cb: function (data) { tts.showRelatedIntents(); },
+ },
+ },
+
+ hooks: {
+ multi: function (selectOrder) {
+ var selectedHost = new Object();
+ for (var index in selectOrder) {
+ if (index == 0) {
+ selectedHost.src = selectOrder[index];
+ } else if (index == 1) {
+ selectedHost.dst = selectOrder[index];
+ }
+ }
+
+ selectedItem = selectedHost;
+
+ tov.addDetailButton('showRelatedTraffic');
+ if(selectOrder.length == 2) {
+ tov.addDetailButton('createHostVtapBtn');
+ }
+ }
+ }
+ };
+
+ // Panel API
+ function createTopoPanel(id, opts) {
+ var p = ps.createPanel(id, opts),
+ pid = id,
+ header, body, footer;
+ p.classed(pCls, true);
+
+ function panel() {
+ return p;
+ }
+
+ function hAppend(x) {
+ return header.append(x);
+ }
+
+ function bAppend(x) {
+ return body.append(x);
+ }
+
+ function fAppend(x) {
+ return footer.append(x);
+ }
+
+ function setup() {
+ p.empty();
+
+ p.append('div').classed('header', true);
+ p.append('div').classed('body', true);
+ p.append('div').classed('footer', true);
+
+ header = p.el().select('.header');
+ body = p.el().select('.body');
+ footer = p.el().select('.footer');
+ }
+
+ function destroy() {
+ ps.destroyPanel(pid);
+ }
+
+ return {
+ panel: panel,
+ setup: setup,
+ destroy: destroy,
+ appendHeader: hAppend,
+ appendBody: bAppend,
+ appendFooter: fAppend,
+ };
+ }
+
+ function hideVtapPanel() {
+ vtap.panel().hide();
+ }
+
+ function destroyVtapPanel() {
+ if(vtap != null) {
+ vtap.destroy();
+ }
+ vtap = null;
+ }
+
+ function addInput(tbody, type, id, label, value) {
+ var tr = tbody.append('tr'),
+ lab;
+ if (typeof label === 'string') {
+ lab = label.replace(/_/g, ' ');
+ } else {
+ lab = label;
+ }
+
+ tr.append('td').attr('class', 'label').text(lab + ' :');
+
+ if (type == 'radio') {
+ var td = tr.append('td');
+ for(var index in value) {
+ if(index == 0) {
+ td.append('input').classed( type + '-input', true)
+ .attr('type', type)
+ .attr('value', value[index])
+ .attr('name', label)
+ .attr('id', id)
+ .attr('checked', 'true');
+ } else {
+ td.append('input').classed( type + '-input', true)
+ .attr('type', type)
+ .attr('name', label)
+ .attr('id', id)
+ .attr('value', value[index]);
+ }
+ td.append('span').text(value[index]);
+ }
+ } else {
+ tr.append('td').append('input').classed(type + '-input', true).attr('type', type)
+ .attr('id', id).attr('value', value);
+ }
+ }
+
+ function addButton(tr, callback, value) {
+ tr.append('td').append('input').classed('button-input', true).attr('type', 'button')
+ .attr('value', value).on('click', callback);
+ }
+
+ function makeButton(callback, text, keyName) {
+ var cb = fs.isF(callback),
+ key = fs.isS(keyName);
+
+ function invoke() {
+ cb && cb();
+ }
+
+ return createDiv('vtap-button')
+ .text(text)
+ .on('click', invoke);
+ }
+
+ function createDiv(cls) {
+ var div = d3.select(document.createElement('div'));
+ if (cls) {
+ div.classed(cls, true);
+ }
+ return div;
+ }
+
+ function addInput(tbody, type, id, label, value) {
+ var tr = tbody.append('tr'),
+ lab;
+ if (typeof label === 'string') {
+ lab = label.replace(/_/g, ' ');
+ } else {
+ lab = label;
+ }
+
+ tr.append('td').attr('class', 'label').text(lab + ' :');
+
+ if (type == 'radio') {
+ var td = tr.append('td');
+ for(var index in value) {
+ if(index == 0) {
+ td.append('input').classed( type + '-input', true)
+ .attr('type', type)
+ .attr('value', value[index])
+ .attr('name', label)
+ .attr('id', id)
+ .attr('checked', 'true');
+ } else {
+ td.append('input').classed( type + '-input', true)
+ .attr('type', type)
+ .attr('name', label)
+ .attr('id', id)
+ .attr('value', value[index]);
+ }
+ td.append('span').text(value[index]);
+ }
+ } else {
+ tr.append('td').append('input').classed(type + '-input', true).attr('type', type)
+ .attr('id', id).attr('value', value);
+ }
+ }
+
+ function addButton(tr, callback, value) {
+ tr.append('td').append('input').classed('button-input', true).attr('type', 'button')
+ .attr('value', value).on('click', callback);
+ }
+
+ function makeButton(callback, text, keyName) {
+ var cb = fs.isF(callback),
+ key = fs.isS(keyName);
+
+ function invoke() {
+ cb && cb();
+ }
+
+ return createDiv('vtap-button')
+ .text(text)
+ .on('click', invoke);
+ }
+
+ function createDiv(cls) {
+ var div = d3.select(document.createElement('div'));
+ if (cls) {
+ div.classed(cls, true);
+ }
+ return div;
+ }
+
+ function displayVtap() {
+ $log.debug("sendEvent openstackVtapIsActivatedRequest: ", selectedItem);
+ wss.sendEvent(osvIsActReq, selectedItem);
+ }
+
+ function respIsActCb(selected) {
+ $log.debug("openstackVtapIsActivatedResponse: ", selected);
+ if(vtap == null) {
+ vtap = createTopoPanel(idVta, vtapPanelOpts);
+ }
+ vtap.setup();
+
+ var svg = vtap.appendHeader('div')
+ .classed('icon', true)
+ .append('svg'),
+ title = vtap.appendHeader('h2'),
+ table = vtap.appendBody('table'),
+ tbody = table.append('tbody'),
+ glyphId = 'm_details';
+
+ gs.addGlyph(svg, glyphId, 30, 0, [1, 1]);
+
+ title.text('Create OpenstackVtap');
+
+ addInput(tbody, 'text', 'srcIp', 'Source IP', selected.srcName);
+ addInput(tbody, 'text', 'dstIp', 'Destination IP', selected.dstName);
+ addInput(tbody, 'radio', 'ipProto', 'Protocol', selected.ipProtoList);
+ addInput(tbody, 'number', 'srcPort', 'Source Port', "");
+ addInput(tbody, 'number', 'dstPort', 'Destination Port', "");
+ addInput(tbody, 'radio', 'vtapType', 'Type', selected.vtapTypeList);
+
+ vtap.appendFooter('hr');
+ var footTr = vtap.appendFooter('table').append('tbody').append('tr');
+
+ addButton(footTr, createVtap, 'Create');
+ addButton(footTr, hideVtapPanel, 'Cancel');
+
+ vtap.panel().show();
+ }
+
+ function createVtap() {
+ var vtapInfo = {};
+
+ vtapInfo.srcIp = document.getElementById('srcIp').value;
+ vtapInfo.dstIp = document.getElementById('dstIp').value;
+ vtapInfo.ipProto = document.querySelector('input[name="Protocol"]:checked').value;
+ vtapInfo.srcPort = document.getElementById('srcPort').value;
+ vtapInfo.dstPort = document.getElementById('dstPort').value;
+ vtapInfo.vtapType = document.querySelector('input[name="Type"]:checked').value;
+
+ if(vtapInfo.srcPort == ""){
+ vtapInfo.srcPort = 0;
+ }
+ if(vtapInfo.dstPort == ""){
+ vtapInfo.dstPort = 0;
+ }
+
+ $log.debug("sendEvent openstackVtapCreateRequest: ", vtapInfo);
+ wss.sendEvent(osvCreateReq, vtapInfo);
+ hideVtapPanel();
+
+ }
+
+ function respCreateCb(result) {
+ $log.debug("respCreateCb: ", result);
+
+ function dOK() {
+ if(result.result == "Failed"){
+ displayVtap(selectedItem);
+ } else {
+ destroyVtapPanel();
+ }
+ }
+
+ function createContent(value) {
+ var content = ds.createDiv();
+ content.append('p').text(value);
+ return content;
+ }
+
+ ds.openDialog(idVDi, vtapPanelOpts)
+ .setTitle("Create Vtap " + result.result)
+ .addContent(createContent(result.value))
+ .addOk(dOK);
+ }
+
+ // invoke code to register with the overlay service
+ angular.module('ovOpenstackvtap', [])
+ .factory('OpenstackVtapTopovService',
+ ['$log', 'FnService', 'WebSocketService', 'GlyphService', 'PanelService', 'DialogService',
+
+ function (_$log_, _fs_, _wss_, _gs_, _ps_, _ds_) {
+ $log = _$log_;
+ fs = _fs_;
+ wss = _wss_;
+ gs = _gs_;
+ ps = _ps_;
+ ds = _ds_;
+
+ var handlers = {},
+ vtapOverlay = 'vtap-overlay',
+ defaultOverlay = 'traffic';
+
+ handlers[osvIsActResp] = respIsActCb;
+ handlers[osvCreateResp] = respCreateCb;
+
+ wss.bindHandlers(handlers);
+
+ return {
+ displayVtap: displayVtap
+ };
+ }])
+ .run(['$log', 'TopoOverlayService', 'OpenstackVtapTopovService', 'TopoTrafficService',
+
+ function (_$log_, _tov_, _ovts_, _tts_) {
+ $log = _$log_;
+ tov = _tov_;
+ ovts = _ovts_;
+ tts = _tts_;
+ tov.register(overlay);
+ }]
+ );
+}());
diff --git a/apps/openstackvtap/app/src/main/resources/definitions/OpenstackVtapNetwork.json b/apps/openstackvtap/app/src/main/resources/definitions/OpenstackVtapNetwork.json
new file mode 100644
index 0000000..32fa001
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/definitions/OpenstackVtapNetwork.json
@@ -0,0 +1,30 @@
+{
+ "type": "object",
+ "required": [
+ "network"
+ ],
+ "properties": {
+ "network": {
+ "type": "object",
+ "required": [
+ "mode",
+ "networkId",
+ "serverIp"
+ ],
+ "properties": {
+ "mode": {
+ "type": "string",
+ "example": "GRE"
+ },
+ "networkId": {
+ "type": "int",
+ "example": 1000
+ },
+ "serverIp": {
+ "type": "string",
+ "example": "10.20.0.1"
+ }
+ }
+ }
+ }
+}
diff --git a/apps/openstackvtap/app/src/main/resources/definitions/dummy.json b/apps/openstackvtap/app/src/main/resources/definitions/dummy.json
deleted file mode 100644
index 7a73a41..0000000
--- a/apps/openstackvtap/app/src/main/resources/definitions/dummy.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
\ No newline at end of file
diff --git a/apps/openstackvtap/app/src/main/resources/gui/css.html b/apps/openstackvtap/app/src/main/resources/gui/css.html
new file mode 100644
index 0000000..9c1889b
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/gui/css.html
@@ -0,0 +1 @@
+<link rel="stylesheet" href="app/view/openstackvtap/openstackvtap.css">
diff --git a/apps/openstackvtap/app/src/main/resources/gui/js.html b/apps/openstackvtap/app/src/main/resources/gui/js.html
new file mode 100644
index 0000000..46a4bb4
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/gui/js.html
@@ -0,0 +1 @@
+<script src="app/view/openstackvtap/openstackvtap.js"></script>