GUI -- TopoView - Reimplemented 'traffic' related functionality.
Change-Id: I86d16324e4ce2cd2e0eb8d8f48f72804d7ce101f
diff --git a/web/gui/src/main/webapp/app/index.html b/web/gui/src/main/webapp/app/index.html
index ccc737b..3b05d23 100644
--- a/web/gui/src/main/webapp/app/index.html
+++ b/web/gui/src/main/webapp/app/index.html
@@ -86,6 +86,7 @@
<script src="view/topo/topoModel.js"></script>
<script src="view/topo/topoPanel.js"></script>
<script src="view/topo/topoSelect.js"></script>
+ <script src="view/topo/topoTraffic.js"></script>
<script src="view/device/device.js"></script>
<!-- TODO: inject javascript refs server-side -->
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 6e3dd5f..24fb013 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -28,7 +28,7 @@
];
// references to injected services etc.
- var $log, fs, ks, zs, gs, ms, sus, tes, tfs, tps, tis, tss;
+ var $log, fs, ks, zs, gs, ms, sus, tes, tfs, tps, tis, tss, tts;
// DOM elements
var ovtopo, svg, defs, zoomLayer, mapG, forceG, noDevsLayer;
@@ -57,12 +57,12 @@
U: [tfs.unpin, 'Unpin node (hover mouse over)'],
R: [resetZoom, 'Reset pan / zoom'],
- //V: [showRelatedIntentsAction, 'Show all related intents'],
- //rightArrow: [showNextIntentAction, 'Show next related intent'],
- //leftArrow: [showPrevIntentAction, 'Show previous related intent'],
- //W: [showSelectedIntentTrafficAction, 'Monitor traffic of selected intent'],
- //A: [showAllTrafficAction, 'Monitor all traffic'],
- //F: [showDeviceLinkFlowsAction, 'Show device link flows'],
+ V: [tts.showRelatedIntentsAction, 'Show all related intents'],
+ rightArrow: [tts.showNextIntentAction, 'Show next related intent'],
+ leftArrow: [tts.showPrevIntentAction, 'Show previous related intent'],
+ W: [tts.showSelectedIntentTrafficAction, 'Monitor traffic of selected intent'],
+ A: [tts.showAllTrafficAction, 'Monitor all traffic'],
+ F: [tts.showDeviceLinkFlowsAction, 'Show device link flows'],
//E: [equalizeMasters, 'Equalize mastership roles'],
@@ -222,11 +222,11 @@
'FnService', 'MastService', 'KeyService', 'ZoomService',
'GlyphService', 'MapService', 'SvgUtilService',
'TopoEventService', 'TopoForceService', 'TopoPanelService',
- 'TopoInstService', 'TopoSelectService',
+ 'TopoInstService', 'TopoSelectService', 'TopoTrafficService',
function ($scope, _$log_, $loc, $timeout, _fs_, mast,
_ks_, _zs_, _gs_, _ms_, _sus_,
- _tes_, _tfs_, _tps_, _tis_, _tss_) {
+ _tes_, _tfs_, _tps_, _tis_, _tss_, _tts_) {
var self = this,
projection,
dim,
@@ -249,6 +249,7 @@
tps = _tps_;
tis = _tis_;
tss = _tss_;
+ tts = _tts_;
self.notifyResize = function () {
svgResized(fs.windowSize(mast.mastHeight()));
diff --git a/web/gui/src/main/webapp/app/view/topo/topoEvent.js b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
index 10ed6df..f04c654 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoEvent.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
@@ -27,7 +27,7 @@
'use strict';
// injected refs
- var $log, wss, wes, tps, tis, tfs, tss;
+ var $log, wss, wes, tps, tis, tfs, tss, tts;
// internal state
var wsock, evApis;
@@ -40,6 +40,8 @@
showDetails: tss,
+ showTraffic: tts,
+
addInstance: tis,
updateInstance: tis,
removeInstance: tis,
@@ -53,8 +55,6 @@
addLink: tfs,
updateLink: tfs,
removeLink: tfs
-
- // TODO: add remaining event api vectors
};
}
@@ -106,9 +106,10 @@
.factory('TopoEventService',
['$log', '$location', 'WebSocketService', 'WsEventService',
'TopoPanelService', 'TopoInstService', 'TopoForceService',
- 'TopoSelectService',
+ 'TopoSelectService', 'TopoTrafficService',
- function (_$log_, $loc, _wss_, _wes_, _tps_, _tis_, _tfs_, _tss_) {
+ function (_$log_, $loc, _wss_, _wes_,
+ _tps_, _tis_, _tfs_, _tss_, _tts_) {
$log = _$log_;
wss = _wss_;
wes = _wes_;
@@ -116,6 +117,7 @@
tis = _tis_;
tfs = _tfs_;
tss = _tss_;
+ tts = _tts_;
bindApis();
diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js
index 87b554e..35c606f 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -23,7 +23,7 @@
'use strict';
// injected refs
- var $log, fs, sus, is, ts, flash, tis, tms, tss, icfg, uplink;
+ var $log, fs, sus, is, ts, flash, tis, tms, tss, tts, icfg, uplink;
// configuration
var labelConfig = {
@@ -996,6 +996,21 @@
return true;
}
+ // ==========================
+ // function entry points for traffic module
+
+ var allTrafficClasses = 'primary secondary animated optical';
+
+ function clearLinkTrafficStyle() {
+ link.style('stroke-width', null)
+ .classed(allTrafficClasses, false);
+ }
+
+ function removeLinkLabels() {
+ network.links.forEach(function (d) {
+ d.label = '';
+ });
+ }
// ==========================
// Module definition
@@ -1018,13 +1033,27 @@
};
}
+ function mkTrafficApi(uplink) {
+ return {
+ clearLinkTrafficStyle: clearLinkTrafficStyle,
+ removeLinkLabels: removeLinkLabels,
+ updateLinks: updateLinks,
+ findLinkById: tms.findLinkById,
+ hovered: tss.hovered,
+ validateSelectionContext: tss.validateSelectionContext,
+ selectOrder: tss.selectOrder,
+ sendEvent: uplink.sendEvent
+ }
+ }
+
angular.module('ovTopo')
.factory('TopoForceService',
['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
'FlashService', 'TopoInstService', 'TopoModelService',
- 'TopoSelectService',
+ 'TopoSelectService', 'TopoTrafficService',
- function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_, _tis_, _tms_, _tss_) {
+ function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_,
+ _tis_, _tms_, _tss_, _tts_) {
$log = _$log_;
fs = _fs_;
sus = _sus_;
@@ -1034,6 +1063,7 @@
tis = _tis_;
tms = _tms_;
tss = _tss_;
+ tts = _tts_;
icfg = is.iconConfig();
@@ -1049,6 +1079,7 @@
tms.initModel(mkModelApi(uplink), dim);
tss.initSelect(mkSelectApi(uplink));
+ tts.initTraffic(mkTrafficApi(uplink));
settings = angular.extend({}, defaultSettings, opts);
@@ -1083,7 +1114,9 @@
}
function destroyForce() {
-
+ tts.destroyTraffic();
+ tss.destroySelect();
+ tms.destroyModel();
}
return {
diff --git a/web/gui/src/main/webapp/app/view/topo/topoModel.js b/web/gui/src/main/webapp/app/view/topo/topoModel.js
index de31eef..b1f02ae 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoModel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoModel.js
@@ -376,9 +376,12 @@
dim = _dim_;
}
+ function destroyModel() { }
+
return {
initModel: initModel,
newDim: newDim,
+ destroyModel: destroyModel,
positionNode: positionNode,
createDeviceNode: createDeviceNode,
diff --git a/web/gui/src/main/webapp/app/view/topo/topoSelect.js b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
index f431d9e..0513477 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoSelect.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
@@ -23,7 +23,7 @@
'use strict';
// injected refs
- var $log, fs, flash, tps;
+ var $log, fs, flash, tps, tts;
// api to topoForce
var api;
@@ -65,7 +65,7 @@
$log.debug("MouseOver()...", m);
if (hovered != m) {
hovered = m;
- requestTrafficForMode();
+ tts.requestTrafficForMode();
}
}
}
@@ -74,7 +74,7 @@
if (!m.dragStarted) {
if (hovered) {
hovered = null;
- requestTrafficForMode();
+ tts.requestTrafficForMode();
}
$log.debug("MouseOut()...", m);
}
@@ -118,8 +118,6 @@
n.classed('selected', true);
api.updateDeviceColors(obj);
updateDetail();
-
- debugSel();
}
function deselectObject(id) {
@@ -130,8 +128,6 @@
fs.removeFromArray(id, selectOrder);
api.updateDeviceColors(obj.obj);
}
-
- debugSel();
}
function deselectAll() {
@@ -141,12 +137,6 @@
selectOrder = [];
api.updateDeviceColors();
updateDetail();
-
- debugSel();
- }
-
- function debugSel() {
- $log.debug(' ..... Selected now >> ', selectOrder);
}
// === -----------------------------------------------------
@@ -175,14 +165,14 @@
function emptySelect() {
haveDetails = false;
tps.hideDetailPanel();
- cancelTraffic();
+ tts.cancelTraffic();
}
function singleSelect() {
// NOTE: detail is shown from 'showDetails' event callback
requestDetails();
- cancelTraffic();
- requestTrafficForMode();
+ tts.cancelTraffic();
+ tts.requestTrafficForMode();
}
function multiSelect() {
@@ -192,17 +182,17 @@
tps.displayMulti(selectOrder);
// always add the 'show traffic' action
- tps.addAction('Show Related Traffic', showRelatedIntentsAction);
+ tps.addAction('Show Related Traffic', tts.showRelatedIntentsAction);
// add other actions, based on what is selected...
if (nSel() === 2 && allSelectionsClass('host')) {
- tps.addAction('Create Host-to-Host Flow', addHostIntentAction);
+ tps.addAction('Create Host-to-Host Flow', tts.addHostIntentAction);
} else if (nSel() >= 2 && allSelectionsClass('host')) {
- tps.addAction('Create Multi-Source Flow', addMultiSourceIntentAction);
+ tps.addAction('Create Multi-Source Flow', tts.addMultiSourceIntentAction);
}
- cancelTraffic();
- requestTrafficForMode();
+ tts.cancelTraffic();
+ tts.requestTrafficForMode();
}
@@ -216,11 +206,11 @@
tps.displaySingle(data);
// always add the 'show traffic' action
- tps.addAction('Show Related Traffic', showRelatedIntentsAction);
+ tps.addAction('Show Related Traffic', tts.showRelatedIntentsAction);
// add other actions, based on what is selected...
if (data.type === 'switch') {
- tps.addAction('Show Device Flows', showDeviceLinkFlowsAction);
+ tps.addAction('Show Device Flows', tts.showDeviceLinkFlowsAction);
}
// only show the details panel if the user hasn't "hidden" it
@@ -242,34 +232,13 @@
}
}
- // === -----------------------------------------------------
- // TODO: migrate these to topoTraffic.js
-
- function cancelTraffic() {
- $log.debug('TODO: cancelTraffic');
-
+ function validateSelectionContext() {
+ if (!hovered && !nSel()) {
+ tts.cancelTraffic();
+ return false;
+ }
+ return true;
}
- function requestTrafficForMode() {
- $log.debug('TODO: requestTrafficForMode');
-
- }
- function showRelatedIntentsAction () {
- $log.debug('TODO: showRelatedIntentsAction');
-
- }
- function addHostIntentAction () {
- $log.debug('TODO: addHostIntentAction');
-
- }
- function addMultiSourceIntentAction () {
- $log.debug('TODO: addMultiSourceIntentAction');
-
- }
- function showDeviceLinkFlowsAction () {
- $log.debug('TODO: showDeviceLinkFlowsAction');
-
- }
-
// === -----------------------------------------------------
// === MODULE DEFINITION ===
@@ -277,12 +246,14 @@
angular.module('ovTopo')
.factory('TopoSelectService',
['$log', 'FnService', 'FlashService', 'TopoPanelService',
+ 'TopoTrafficService',
- function (_$log_, _fs_, _flash_, _tps_) {
+ function (_$log_, _fs_, _flash_, _tps_, _tts_) {
$log = _$log_;
fs = _fs_;
flash = _flash_;
tps = _tps_;
+ tts = _tts_;
function initSelect(_api_) {
api = _api_;
@@ -302,8 +273,11 @@
selectObject: selectObject,
deselectObject: deselectObject,
deselectAll: deselectAll,
+
hovered: function () { return hovered; },
- haveDetails: function () { return haveDetails; }
+ haveDetails: function () { return haveDetails; },
+ selectOrder: function () { return selectOrder; },
+ validateSelectionContext: validateSelectionContext
};
}]);
}());
diff --git a/web/gui/src/main/webapp/app/view/topo/topoTraffic.js b/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
new file mode 100644
index 0000000..b1563ab
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 -- Topology Traffic Module.
+ Defines behavior for viewing different traffic modes.
+ */
+
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, fs, flash;
+
+ // api to topoForce
+ var api;
+ /*
+ clearLinkTrafficStyle()
+ removeLinkLabels()
+ updateLinks()
+ findLinkById( id )
+ hovered()
+ validateSelectionContext()
+ sendEvent( type, {payload} )
+ */
+
+ // constants
+ var hoverModeNone = 0,
+ hoverModeAll = 1,
+ hoverModeFlows = 2,
+ hoverModeIntents = 3;
+
+ // internal state
+ var hoverMode = hoverModeNone;
+
+
+ // === -----------------------------------------------------
+ // Event Handlers
+
+ function showTraffic(data) {
+ var paths = data.paths;
+
+ api.clearLinkTrafficStyle();
+ api.removeLinkLabels();
+
+ // Now highlight all links in the paths payload, and attach
+ // labels to them, if they are defined.
+ paths.forEach(function (p) {
+ var n = p.links.length,
+ i, ldata;
+
+ for (i=0; i<n; i++) {
+ ldata = api.findLinkById(p.links[i]);
+ if (ldata && ldata.el) {
+ ldata.el.classed(p.class, true);
+ ldata.label = p.labels[i];
+ }
+ }
+ });
+
+ api.updateLinks();
+ }
+
+ // === -----------------------------------------------------
+ // Helper functions
+
+ function requestDeviceLinkFlows() {
+ var hov = api.hovered();
+
+ function hoverValid() {
+ return hoverMode === hoverModeFlows &&
+ hov && (hov.class === 'device');
+ }
+
+ if (api.validateSelectionContext()) {
+ api.sendEvent('requestDeviceLinkFlows', {
+ ids: api.selectOrder(),
+ hover: hoverValid() ? hov.id : ''
+ });
+ }
+ }
+
+ function requestRelatedIntents() {
+ var hov = api.hovered();
+
+ function hoverValid() {
+ return hoverMode === hoverModeIntents &&
+ hov && (hov.class === 'host' || hov.class === 'device');
+ }
+
+ if (api.validateSelectionContext()) {
+ api.sendEvent('requestRelatedIntents', {
+ ids: api.selectOrder(),
+ hover: hoverValid() ? hov.id : ''
+ });
+ }
+ }
+
+
+ // === -----------------------------------------------------
+ // Traffic requests
+
+ function cancelTraffic() {
+ api.sendEvent('cancelTraffic');
+ }
+
+ // invoked in response to change in selection and/or mouseover/out:
+ function requestTrafficForMode() {
+ if (hoverMode === hoverModeFlows) {
+ requestDeviceLinkFlows();
+ } else if (hoverMode === hoverModeIntents) {
+ requestRelatedIntents();
+ }
+ }
+
+ // === -----------------------------
+ // keystroke commands
+
+ // keystroke-right-arrow (see topo.js)
+ function showNextIntentAction() {
+ hoverMode = hoverModeNone;
+ api.sendEvent('requestNextRelatedIntent');
+ flash.flash('>');
+ }
+
+ // keystroke-left-arrow (see topo.js)
+ function showPrevIntentAction() {
+ hoverMode = hoverModeNone;
+ api.sendEvent('requestPrevRelatedIntent');
+ flash.flash('<');
+ }
+
+ // keystroke-W (see topo.js)
+ function showSelectedIntentTrafficAction() {
+ hoverMode = hoverModeNone;
+ api.sendEvent('requestSelectedIntentTraffic');
+ flash.flash('Traffic on Selected Path');
+ }
+
+ // keystroke-A (see topo.js)
+ function showAllTrafficAction() {
+ hoverMode = hoverModeAll;
+ api.sendEvent('requestAllTraffic');
+ flash.flash('All Traffic');
+ }
+
+ // === -----------------------------
+ // action buttons on detail panel
+
+ // also, keystroke-V (see topo.js)
+ function showRelatedIntentsAction () {
+ hoverMode = hoverModeIntents;
+ requestRelatedIntents();
+ flash.flash('Related Paths');
+ }
+
+ function addHostIntentAction () {
+ var so = api.selectOrder();
+ api.sendEvent('addHostIntent', {
+ one: so[0],
+ two: so[1],
+ ids: so
+ });
+ flash.flash('Host-to-Host flow added');
+ }
+
+ function addMultiSourceIntentAction () {
+ var so = api.selectOrder();
+ api.sendEvent('addMultiSourceIntent', {
+ src: so.slice(0, so.length - 1),
+ dst: so[so.length - 1],
+ ids: so
+ });
+ flash.flash('Multi-Source flow added');
+ }
+
+ // also, keystroke-F (see topo.js)
+ function showDeviceLinkFlowsAction () {
+ hoverMode = hoverModeFlows;
+ requestDeviceLinkFlows();
+ flash.flash('Device Flows');
+ }
+
+
+ // === -----------------------------------------------------
+ // === MODULE DEFINITION ===
+
+ angular.module('ovTopo')
+ .factory('TopoTrafficService',
+ ['$log', 'FnService', 'FlashService',
+
+ function (_$log_, _fs_, _flash_) {
+ $log = _$log_;
+ fs = _fs_;
+ flash = _flash_;
+
+ function initTraffic(_api_) {
+ api = _api_;
+ }
+
+ function destroyTraffic() { }
+
+ return {
+ initTraffic: initTraffic,
+ destroyTraffic: destroyTraffic,
+
+ showTraffic: showTraffic,
+
+ cancelTraffic: cancelTraffic,
+ requestTrafficForMode: requestTrafficForMode,
+ showRelatedIntentsAction: showRelatedIntentsAction,
+ addHostIntentAction: addHostIntentAction,
+ addMultiSourceIntentAction: addMultiSourceIntentAction,
+ showDeviceLinkFlowsAction: showDeviceLinkFlowsAction,
+ showNextIntentAction: showNextIntentAction,
+ showPrevIntentAction: showPrevIntentAction,
+ showSelectedIntentTrafficAction: showSelectedIntentTrafficAction,
+ showAllTrafficAction: showAllTrafficAction
+ };
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
index 67291f3..84c1f61 100644
--- a/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
@@ -207,7 +207,7 @@
it('should define api functions', function () {
expect(fs.areFunctions(tms, [
- 'initModel', 'newDim',
+ 'initModel', 'newDim', 'destroyModel',
'positionNode', 'createDeviceNode', 'createHostNode',
'createHostLink', 'createLink',
'coordFromLngLat', 'lngLatFromCoord',
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js
index e640764..78bde80 100644
--- a/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js
@@ -36,7 +36,8 @@
expect(fs.areFunctions(tss, [
'initSelect', 'destroySelect', 'showDetails', 'toggleDetails',
'nodeMouseOver', 'nodeMouseOut', 'selectObject', 'deselectObject',
- 'deselectAll', 'hovered', 'haveDetails'
+ 'deselectAll', 'hovered', 'haveDetails', 'selectOrder',
+ 'validateSelectionContext'
])).toBeTruthy();
});
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoTraffic-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoTraffic-spec.js
new file mode 100644
index 0000000..ecc4a34
--- /dev/null
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoTraffic-spec.js
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 -- Topo View -- Topo Traffic Service - Unit Tests
+ */
+describe('factory: view/topo/topoTraffic.js', function() {
+ var $log, fs, tts;
+
+ beforeEach(module('ovTopo', 'onosUtil', 'onosLayer'));
+
+ beforeEach(inject(function (_$log_, FnService, TopoTrafficService) {
+ $log = _$log_;
+ fs = FnService;
+ tts = TopoTrafficService;
+ }));
+
+ it('should define TopoTrafficService', function () {
+ expect(tts).toBeDefined();
+ });
+
+ it('should define api functions', function () {
+ expect(fs.areFunctions(tts, [
+ 'initTraffic', 'destroyTraffic', 'showTraffic',
+ 'cancelTraffic', 'requestTrafficForMode',
+ 'showRelatedIntentsAction', 'addHostIntentAction',
+ 'addMultiSourceIntentAction', 'showDeviceLinkFlowsAction',
+ 'showNextIntentAction', 'showPrevIntentAction',
+ 'showSelectedIntentTrafficAction', 'showAllTrafficAction'
+ ])).toBeTruthy();
+ });
+
+ // TODO: more tests...
+});