ONOS-5726: augmented implementation of "showIntent" overlay support.
- added acceptIntent() callback hook, to allow overlays to declare which intent types they can display.
Change-Id: I18d0b6f05b0a348623bd5a90d58d996d389bdd95
diff --git a/core/api/src/main/java/org/onosproject/ui/topo/AbstractTopoMonitor.java b/core/api/src/main/java/org/onosproject/ui/topo/AbstractTopoMonitor.java
new file mode 100644
index 0000000..e6b7cfa
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/topo/AbstractTopoMonitor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017-present 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.
+ */
+
+package org.onosproject.ui.topo;
+
+/**
+ * Base class for the business logic of topology overlay "monitors".
+ */
+public class AbstractTopoMonitor {
+
+ // TODO: pull common code up into this class
+
+ // Note to Andrea:
+ // this class has to be defined in the core.api module, because
+ // external applications may want to extend it.
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java
index b2ba156..e97b7dd 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java
@@ -44,6 +44,7 @@
import org.onosproject.ui.impl.topo.util.TrafficLink;
import org.onosproject.ui.impl.topo.util.TrafficLink.StatsType;
import org.onosproject.ui.impl.topo.util.TrafficLinkMap;
+import org.onosproject.ui.topo.AbstractTopoMonitor;
import org.onosproject.ui.topo.DeviceHighlight;
import org.onosproject.ui.topo.Highlights;
import org.onosproject.ui.topo.Highlights.Amount;
@@ -74,7 +75,7 @@
/**
* Encapsulates the behavior of monitoring specific traffic patterns.
*/
-public class TrafficMonitor {
+public class TrafficMonitor extends AbstractTopoMonitor {
// 4 Kilo Bytes as threshold
private static final double BPS_THRESHOLD = 4 * TopoUtils.KILO;
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/TopoIntentFilter.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/TopoIntentFilter.java
index 54a43d4..e43fbf2 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/TopoIntentFilter.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/TopoIntentFilter.java
@@ -252,7 +252,7 @@
return false;
}
- // Indicates whether the specified flow rules involvesthe given device.
+ // Indicates whether the specified flow rules involves the given device.
private boolean rulesContainDevice(Collection<FlowRule> flowRules, DeviceId id) {
for (FlowRule rule : flowRules) {
if (rule.deviceId().equals(id)) {
diff --git a/web/gui/src/main/webapp/app/view/intent/intent.html b/web/gui/src/main/webapp/app/view/intent/intent.html
index 95fe5ca..d5f52d6 100644
--- a/web/gui/src/main/webapp/app/view/intent/intent.html
+++ b/web/gui/src/main/webapp/app/view/intent/intent.html
@@ -28,7 +28,7 @@
<div class="separator"></div>
<div class="show-intent-btn">
- <div ng-class="{active: !!selId}"
+ <div ng-class="{active: canShowIntent()}"
icon icon-id="topo" icon-size="42"
tooltip tt-msg="topoTip"
ng-click="showIntent()">
diff --git a/web/gui/src/main/webapp/app/view/intent/intent.js b/web/gui/src/main/webapp/app/view/intent/intent.js
index a2375ba..9567119 100644
--- a/web/gui/src/main/webapp/app/view/intent/intent.js
+++ b/web/gui/src/main/webapp/app/view/intent/intent.js
@@ -21,52 +21,154 @@
(function () {
'use strict';
+ // constants and configuration
var dialogId = 'remove-intent-dialog',
dialogOpts = {
edge: 'right'
- },
- dropdown;
+ };
+
+ // DOM elements
+ var dropdown;
+
+ // injected refs
+ var $log, $scope, ns, tov, tts, ds;
+
+
+ function initScope() {
+ $scope.topoTip = 'Show selected intent on topology view';
+ $scope.resubmitTip = 'Resubmit selected intent';
+ $scope.deactivateTip = 'Remove selected intent';
+ $scope.purgeTip = 'Purge selected intent';
+ $scope.purgeAllTip = 'Purge withdrawn intents';
+
+ $scope.briefTip = 'Switch to brief view';
+ $scope.detailTip = 'Switch to detailed view';
+
+ $scope.brief = true;
+ $scope.intentState = 'NA';
+ $scope.fired = false;
+ }
+
+ // === row selection and response callback functions:
+
+ function selCb($event, row) {
+ $log.debug('Got a click on:', row);
+ var m = /(\d+)\s:\s(.*)/.exec(row.appId),
+ id = m ? m[1] : null,
+ name = m ? m[2] : null;
+
+ $scope.intentData = ($scope.selId && m) ? {
+ appId: id,
+ appName: name,
+ key: row.key,
+ intentType: row.type
+ } : null;
+
+ $scope.intentState = row.state;
+ showDropdown(false);
+ }
+
+ function respCb() {
+ if ($scope.fired) {
+ if ($scope.changedData) {
+ $scope.intentState = $scope.changedData.state;
+ }
+ $scope.fired = false;
+ }
+ }
+
+
+ // === show-intent functions
+
+ function showDropdown(b) {
+ dropdown.style('display', b ? 'block' : 'none');
+ }
+
+ function showIntent () {
+ var d = $scope.intentData,
+ handlers,
+ nh;
+
+ if (!d) {
+ // no intent selected - nothing to do
+ return;
+ }
+
+ function setOvAndNavigate(info) {
+ d.overlayId = info.id;
+ ns.navTo('topo', d);
+ }
+
+ function clickMe(data) {
+ showDropdown(false);
+ setOvAndNavigate(data);
+ }
+
+ function setUpSelection(handlers) {
+ dropdown.text(null);
+
+ handlers.forEach(function (data) {
+ var div = dropdown.append('div');
+ div.classed('overlay-choice', true);
+ div.text(data.tt);
+ div.on('click', function () {
+ clickMe(data);
+ });
+ });
+
+ showDropdown(true);
+ }
+
+ handlers = tov.overlaysAcceptingIntents(d.intentType);
+ nh = handlers.length;
+
+ if (nh === 1) {
+ setOvAndNavigate(handlers[0]);
+
+ } else if (nh > 1) {
+ // let the user choose which overlay to invoke...
+ setUpSelection(handlers);
+
+ } else {
+ $log.warn('Sorry - no overlay configured to show',
+ 'intents of type', d.intentType);
+ }
+ }
+
+
+ // === intent action functionality
+
+ // TODO: refactor to move functions below to here...
+
+
+ // === intent view controller
angular.module('ovIntent', [])
.controller('OvIntentCtrl',
['$log', '$scope', 'TableBuilderService', 'NavService',
'TopoOverlayService', 'TopoTrafficService', 'DialogService',
- function ($log, $scope, tbs, ns, tov, tts, ds) {
- $scope.briefTip = 'Switch to brief view';
- $scope.detailTip = 'Switch to detailed view';
- $scope.brief = true;
- $scope.intentState = 'NA';
- $scope.fired = false;
- $scope.showOverlays = false;
+ function (_$log_, _$scope_, tbs, _ns_, _tov_, _tts_, _ds_) {
+ $log = _$log_;
+ $scope = _$scope_;
+ ns = _ns_;
+ tov = _tov_;
+ tts = _tts_;
+ ds = _ds_;
+
+ initScope();
dropdown = d3.select('div.show-intent-btn .dropdown');
- function selCb($event, row) {
- $log.debug('Got a click on:', row);
- var m = /(\d+)\s:\s(.*)/.exec(row.appId),
- id = m ? m[1] : null,
- name = m ? m[2] : null;
+ // set up scope function references...
+ $scope.showIntent = showIntent;
- $scope.intentData = ($scope.selId && m) ? {
- appId: id,
- appName: name,
- key: row.key
- } : null;
+ $scope.canShowIntent = function() {
+ var d = $scope.intentData;
+ return d && tov.overlaysAcceptingIntents(d.intentType).length > 0;
+ };
- $scope.intentState = row.state;
- showDropdown(false);
- }
-
- function respCb() {
- if ($scope.fired) {
- if ($scope.changedData) {
- $scope.intentState = $scope.changedData.state;
- }
- $scope.fired = false;
- }
- }
-
+ // build the table
tbs.buildTable({
scope: $scope,
tag: 'intent',
@@ -75,70 +177,6 @@
idKey: 'key'
});
- $scope.topoTip = 'Show selected intent on topology view';
- $scope.resubmitTip = 'Resubmit selected intent';
- $scope.deactivateTip = 'Remove selected intent';
- $scope.purgeTip = 'Purge selected intent';
- $scope.purgeAllTip = 'Purge withdrawn intents';
-
-
- function showDropdown(b) {
- dropdown.style('display', b ? 'block' : 'none');
- }
-
- $scope.showIntent = function () {
- var d = $scope.intentData,
- tovData,
- ncb;
-
- if (!d) {
- // no intent selected - nothing to do
- return;
- }
-
- function setOvAndNavigate(info) {
- d.overlayId = info.id;
- ns.navTo('topo', d);
- }
-
- function clickMe(data) {
- showDropdown(false);
- setOvAndNavigate(data);
- }
-
- function setUpSelection(tovData) {
- dropdown.text(null);
-
- tovData.forEach(function (data) {
- var div = dropdown.append('div');
- div.classed('overlay-choice', true);
- div.text(data.tt);
- div.on('click', function () {
- clickMe(data);
- });
- });
-
- showDropdown(true);
- }
-
- tovData = tov.listOverlaysThatShowIntents();
- ncb = tovData.length;
- // NOTE: ncb should be at least 1, (traffic overlay)
-
- if (ncb === 1) {
- setOvAndNavigate(tovData[0]);
-
- } else if (ncb > 1) {
- // let the user choose which overlay to invoke...
- setUpSelection(tovData);
-
- } else {
- $log.error('Internal Error - no overlay configured',
- 'to show selected intent on topology view');
- }
- };
-
-
// TODO: clean up the following code...
@@ -191,6 +229,7 @@
.addCancel(dCancel)
.bindKeys();
}
+
function executeActions(action) {
var content = ds.createDiv(),
txt='purgeIntents';
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 1762cf4..0623478 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -603,11 +603,13 @@
setMap: setMap
});
+ // pull intent data from the query string...
if (params.key && params.appId && params.appName) {
$scope.intentData = {
key: params.key,
appId: params.appId,
- appName: params.appName
+ appName: params.appName,
+ intentType: params.intentType
};
}
diff --git a/web/gui/src/main/webapp/app/view/topo/topoOverlay.js b/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
index ad42470..d51d8fc 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
@@ -113,18 +113,20 @@
return d3.map(overlays).keys();
}
- // returns data on overlays that implement the showIntent callback
- function listShowIntents() {
+ // Returns an array containing overlays that implement the showIntent and
+ // acceptIntent callbacks, and that accept the given intent type
+ function overlaysAcceptingIntents(intentType) {
var result = [];
angular.forEach(overlays, function (ov) {
- var hooks = fs.isO(ov.hooks) || {},
- sicb = fs.isF(hooks.showintent);
+ var ovid = ov.overlayId,
+ hooks = fs.isO(ov.hooks) || {},
+ aicb = fs.isF(hooks.acceptIntent),
+ sicb = fs.isF(hooks.showIntent);
- if (sicb) {
+ if (sicb && aicb && aicb(intentType)) {
result.push({
- id: ov.overlayId,
- tt: ov.tooltip || '%' + ov.overlayId + '%',
- gid: ov._glyphId
+ id: ovid,
+ tt: ov.tooltip || '%' + ovid + '%'
});
}
});
@@ -304,7 +306,7 @@
// Request from Intent View to visualize an intent on the topo view
function showIntentHook(intentData) {
- var cb = _hook('showintent');
+ var cb = _hook('showIntent');
return cb && cb(intentData);
}
@@ -446,7 +448,7 @@
register: register,
setApi: setApi,
list: list,
- listOverlaysThatShowIntents: listShowIntents,
+ overlaysAcceptingIntents: overlaysAcceptingIntents,
augmentRbset: augmentRbset,
mkGlyphId: mkGlyphId,
tbSelection: tbSelection,
diff --git a/web/gui/src/main/webapp/app/view/topo/topoTrafficNew.js b/web/gui/src/main/webapp/app/view/topo/topoTrafficNew.js
index 312b652..ac8f4a2 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoTrafficNew.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoTrafficNew.js
@@ -143,8 +143,12 @@
tts.requestTrafficForMode(true);
},
- // intent visualization hook
- showintent: function (info) {
+ // intent visualization hooks
+ acceptIntent: function (type) {
+ // accept any intent type except "Protected" intents
+ return (!type.startsWith('Protected'));
+ },
+ showIntent: function (info) {
$log.debug('^^ trafficOverlay.showintent() ^^', info);
tts.selectIntent(info);
}