GUI -- Major rework to link processing so that we consolidate links A->B and B->A into a single backing object.
- added blue glow to ONOS instance when showing switch affinity.
Change-Id: Ia2a52d9d0571bc8c5eed964c85862f5798c7c5db
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_10_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_10_onos.json
new file mode 100644
index 0000000..5775e43
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_10_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "addLink",
+ "payload": {
+ "id": "of:0000ffffffff0008/10-of:0000ffffffff0003/20",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000ffffffff0008",
+ "srcPort": "10",
+ "dst": "of:0000ffffffff0003",
+ "dstPort": "20",
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_11_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_11_onos.json
new file mode 100644
index 0000000..f0d0b4d
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_11_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "addHost",
+ "payload": {
+ "id": "0E:2A:69:30:13:88/-1",
+ "ingress": "0E:2A:69:30:13:88/-1/0-of:0000ffffffff0003/1",
+ "egress": "of:0000ffffffff0003/1-0E:2A:69:30:13:88/-1/0",
+ "cp": {
+ "device": "of:0000ffffffff0003",
+ "port": 1
+ },
+ "labels": [
+ "Host-A",
+ "0E:2A:69:30:13:88"
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_12_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_12_onos.json
new file mode 100644
index 0000000..d977343
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_12_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "addHost",
+ "payload": {
+ "id": "0E:2A:69:30:13:89/-1",
+ "ingress": "0E:2A:69:30:13:89/-1/0-of:0000ffffffff0007/1",
+ "egress": "of:0000ffffffff0007/1-0E:2A:69:30:13:89/-1/0",
+ "cp": {
+ "device": "of:0000ffffffff0007",
+ "port": 1
+ },
+ "labels": [
+ "Host-B",
+ "0E:2A:69:30:13:89"
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_13_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_13_onos.json
new file mode 100644
index 0000000..8f643ba
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_13_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "addHost",
+ "payload": {
+ "id": "0E:2A:69:30:13:8A/-1",
+ "ingress": "0E:2A:69:30:13:8A/-1/0-of:0000ffffffff0008/1",
+ "egress": "of:0000ffffffff0008/1-0E:2A:69:30:13:8A/-1/0",
+ "cp": {
+ "device": "of:0000ffffffff0008",
+ "port": 1
+ },
+ "labels": [
+ "Host-C",
+ "0E:2A:69:30:13:8A"
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_14_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_14_onos.json
new file mode 100644
index 0000000..d761019
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_14_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "updateLink",
+ "payload": {
+ "id": "of:0000ffffffff0007/10-of:0000ffffffff0008/20",
+ "src": "of:0000ffffffff0007",
+ "srcPort": "10",
+ "dst": "of:0000ffffffff0008",
+ "dstPort": "20",
+
+ "type": "direct",
+ "linkWidth": 2,
+ "online": true,
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_15_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_15_onos.json
new file mode 100644
index 0000000..dbdfb3a
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_15_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "updateLink",
+ "payload": {
+ "id": "of:0000ffffffff0007/20-of:0000ffffffff0003/10",
+ "src": "of:0000ffffffff0007",
+ "srcPort": "20",
+ "dst": "of:0000ffffffff0003",
+ "dstPort": "10",
+
+ "type": "direct",
+ "linkWidth": 6,
+ "online": true,
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_16_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_16_onos.json
new file mode 100644
index 0000000..b7783c1
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_16_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "removeLink",
+ "payload": {
+ "id": "of:0000ffffffff0007/20-of:0000ffffffff0003/10",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000ffffffff0007",
+ "srcPort": "20",
+ "dst": "of:0000ffffffff0003",
+ "dstPort": "10",
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_17_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_17_onos.json
new file mode 100644
index 0000000..daf926e
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_17_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "removeLink",
+ "payload": {
+ "id": "of:0000ffffffff0003/10-of:0000ffffffff0007/20",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000ffffffff0003",
+ "srcPort": "10",
+ "dst": "of:0000ffffffff0007",
+ "dstPort": "20",
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_1_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_1_onos.json
new file mode 100644
index 0000000..d7f69d3
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_1_onos.json
@@ -0,0 +1,11 @@
+{
+ "event": "addInstance",
+ "payload": {
+ "id": "local",
+ "online": true,
+ "labels": [
+ "local",
+ "127.0.0.1"
+ ]
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_2_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_2_onos.json
new file mode 100644
index 0000000..352a835
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_2_onos.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000ffffffff0003",
+ "type": "switch",
+ "online": true,
+ "master": "local",
+ "labels": [
+ "0000ffffffff0003",
+ "FF:FF:FF:FF:00:03",
+ "sw-3"
+ ],
+ "metaUi": {
+ "x": 282,
+ "y": 503
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_3_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_3_onos.json
new file mode 100644
index 0000000..d52db4e
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_3_onos.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000ffffffff0007",
+ "type": "switch",
+ "online": true,
+ "master": "local",
+ "labels": [
+ "0000ffffffff0007",
+ "FF:FF:FF:FF:00:07",
+ "sw-7"
+ ],
+ "metaUi": {
+ "x": 530,
+ "y": 330
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_4_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_4_onos.json
new file mode 100644
index 0000000..9f2c260
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_4_onos.json
@@ -0,0 +1,18 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000ffffffff0008",
+ "type": "switch",
+ "online": true,
+ "master": "local",
+ "labels": [
+ "0000ffffffff0008",
+ "FF:FF:FF:FF:00:08",
+ "sw-8"
+ ],
+ "metaUi": {
+ "x": 734,
+ "y": 477
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_5_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_5_onos.json
new file mode 100644
index 0000000..771c332
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_5_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "addLink",
+ "payload": {
+ "id": "of:0000ffffffff0007/10-of:0000ffffffff0008/20",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000ffffffff0007",
+ "srcPort": "10",
+ "dst": "of:0000ffffffff0008",
+ "dstPort": "20",
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_6_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_6_onos.json
new file mode 100644
index 0000000..6eea869
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_6_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "addLink",
+ "payload": {
+ "id": "of:0000ffffffff0008/20-of:0000ffffffff0007/10",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000ffffffff0008",
+ "srcPort": "20",
+ "dst": "of:0000ffffffff0007",
+ "dstPort": "10",
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_7_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_7_onos.json
new file mode 100644
index 0000000..cff94a5
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_7_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "addLink",
+ "payload": {
+ "id": "of:0000ffffffff0003/10-of:0000ffffffff0007/20",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000ffffffff0003",
+ "srcPort": "10",
+ "dst": "of:0000ffffffff0007",
+ "dstPort": "20",
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_8_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_8_onos.json
new file mode 100644
index 0000000..0a5a314
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_8_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "addLink",
+ "payload": {
+ "id": "of:0000ffffffff0007/20-of:0000ffffffff0003/10",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000ffffffff0007",
+ "srcPort": "20",
+ "dst": "of:0000ffffffff0003",
+ "dstPort": "10",
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_9_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_9_onos.json
new file mode 100644
index 0000000..0b6b67b
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_9_onos.json
@@ -0,0 +1,15 @@
+{
+ "event": "addLink",
+ "payload": {
+ "id": "of:0000ffffffff0003/20-of:0000ffffffff0008/10",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000ffffffff0003",
+ "srcPort": "20",
+ "dst": "of:0000ffffffff0008",
+ "dstPort": "10",
+ "props" : {
+ "BW": "90 Gb"
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/links/scenario.json b/web/gui/src/main/webapp/json/ev/links/scenario.json
new file mode 100644
index 0000000..b1988ca
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/scenario.json
@@ -0,0 +1,16 @@
+{
+ "comments": [
+ "Stepping through link events"
+ ],
+ "title": "Process Link Events Scenario",
+ "params": {
+ "lastAuto": 13
+ },
+ "description": [
+ "Develop link event handling.",
+ "",
+ "Press 'S' to load initial events.",
+ "",
+ "Press spacebar to complete the scenario..."
+ ]
+}
diff --git a/web/gui/src/main/webapp/topo2.css b/web/gui/src/main/webapp/topo2.css
index 2eb6a8e..35ddc5a 100644
--- a/web/gui/src/main/webapp/topo2.css
+++ b/web/gui/src/main/webapp/topo2.css
@@ -262,6 +262,7 @@
}
#topo-oibox .onosInst.mastership.affinity {
opacity: 1.0;
+ box-shadow: 0px 2px 8px #33e;
}
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index 37e098e..0d84125 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -339,13 +339,16 @@
link: {
hostLink: 'pkt',
direct: 'pkt',
+ indirect: '',
+ tunnel: '',
optical: 'opt'
}
};
function inLayer(d, layer) {
- var look = layerLookup[d.class],
- lyr = look && look[d.type];
+ var type = d.class === 'link' ? d.type() : d.type,
+ look = layerLookup[d.class],
+ lyr = look && look[type];
return lyr === layer;
}
@@ -408,6 +411,115 @@
});
}
+ function makeNodeKey(d, what) {
+ var port = what + 'Port';
+ return d[what] + '/' + d[port];
+ }
+
+ function makeLinkKey(d, flipped) {
+ var one = flipped ? makeNodeKey(d, 'dst') : makeNodeKey(d, 'src'),
+ two = flipped ? makeNodeKey(d, 'src') : makeNodeKey(d, 'dst');
+ return one + '-' + two;
+ }
+
+ function findLink(linkData, op) {
+ var key = makeLinkKey(linkData),
+ keyrev = makeLinkKey(linkData, 1),
+ link = network.lookup[key],
+ linkRev = network.lookup[keyrev],
+ result = {},
+ ldata = link || linkRev,
+ rawLink;
+
+ if (op === 'add') {
+ if (link) {
+ // trying to add a link that we already know about
+ result.ldata = link;
+ result.badLogic = 'addLink: link already added';
+
+ } else if (linkRev) {
+ // we found the reverse of the link to be added
+ result.ldata = linkRev;
+ if (linkRev.fromTarget) {
+ result.badLogic = 'addLink: link already added';
+ }
+ }
+ } else if (op === 'update') {
+ if (!ldata) {
+ result.badLogic = 'updateLink: link not found';
+ } else {
+ rawLink = link ? ldata.fromSource : ldata.fromTarget;
+ result.updateWith = function (data) {
+ $.extend(rawLink, data);
+ restyleLinkElement(ldata);
+ }
+ }
+ } else if (op === 'remove') {
+ if (!ldata) {
+ result.badLogic = 'removeLink: link not found';
+ } else {
+ rawLink = link ? ldata.fromSource : ldata.fromTarget;
+
+ if (!rawLink) {
+ result.badLogic = 'removeLink: link not found';
+
+ } else {
+ result.removeRawLink = function () {
+ if (link) {
+ // remove fromSource
+ ldata.fromSource = null;
+ if (ldata.fromTarget) {
+ // promote target into source position
+ ldata.fromSource = ldata.fromTarget;
+ ldata.fromTarget = null;
+ ldata.key = keyrev;
+ delete network.lookup[key];
+ network.lookup[keyrev] = ldata;
+ }
+ } else {
+ // remove fromTarget
+ ldata.fromTarget = null;
+ }
+ if (ldata.fromSource) {
+ restyleLinkElement(ldata);
+ } else {
+ removeLinkElement(ldata);
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ function addLinkUpdate(ldata, link) {
+ // add link event, but we already have the reverse link installed
+ ldata.fromTarget = link;
+ restyleLinkElement(ldata);
+ }
+
+ var allLinkTypes = 'direct indirect optical tunnel',
+ defaultLinkType = 'direct';
+
+ function restyleLinkElement(ldata) {
+ // this fn's job is to look at raw links and decide what svg classes
+ // need to be applied to the line element in the DOM
+ var el = ldata.el,
+ type = ldata.type(),
+ lw = ldata.linkWidth(),
+ online = ldata.online();
+
+ el.classed('link', true);
+ el.classed('inactive', !online);
+ el.classed(allLinkTypes, false);
+ if (type) {
+ el.classed(type, true);
+ }
+ el.transition()
+ .duration(1000)
+ .attr('stroke-width', linkScale(lw))
+ .attr('stroke', '#666'); // TODO: remove explicit stroke (use CSS)
+ }
// ==============================
// Event handlers for server-pushed events
@@ -465,10 +577,26 @@
function addLink(data) {
evTrace(data);
var link = data.payload,
- lnk = createLink(link);
- if (lnk) {
- network.links.push(lnk);
- network.lookup[lnk.id] = lnk;
+ result = findLink(link, 'add'),
+ bad = result.badLogic,
+ ldata = result.ldata;
+
+ if (bad) {
+ logicError(bad + ': ' + link.id);
+ return;
+ }
+
+ if (ldata) {
+ // we already have a backing store link for src/dst nodes
+ addLinkUpdate(ldata, link);
+ return;
+ }
+
+ // no backing store link yet
+ ldata = createLink(link);
+ if (ldata) {
+ network.links.push(ldata);
+ network.lookup[ldata.key] = ldata;
updateLinks();
network.force.start();
}
@@ -511,14 +639,13 @@
function updateLink(data) {
evTrace(data);
var link = data.payload,
- id = link.id,
- linkData = network.lookup[id];
- if (linkData) {
- $.extend(linkData, link);
- updateLinkState(linkData);
- } else {
- logicError('updateLink lookup fail. ID = "' + id + '"');
+ result = findLink(link, 'update'),
+ bad = result.badLogic;
+ if (bad) {
+ logicError(bad + ': ' + link.id);
+ return;
}
+ result.updateWith(link);
}
function updateHost(data) {
@@ -538,13 +665,13 @@
function removeLink(data) {
evTrace(data);
var link = data.payload,
- id = link.id,
- linkData = network.lookup[id];
- if (linkData) {
- removeLinkElement(linkData);
- } else {
- logicError('removeLink lookup fail. ID = "' + id + '"');
+ result = findLink(link, 'remove'),
+ bad = result.badLogic;
+ if (bad) {
+ logicError(bad + ': ' + link.id);
+ return;
}
+ result.removeRawLink();
}
function removeHost(data) {
@@ -805,11 +932,13 @@
// Synthesize link ...
$.extend(lnk, {
- id: id,
+ key: id,
class: 'link',
- type: 'hostLink',
- svgClass: 'link hostLink',
- linkWidth: 1
+
+ type: function () { return 'hostLink'; },
+ // TODO: ideally, we should see if our edge switch is online...
+ online: function () { return true; },
+ linkWidth: function () { return 1; }
});
return lnk;
}
@@ -822,10 +951,29 @@
return null;
}
- // merge in remaining data
- $.extend(lnk, link, {
+ $.extend(lnk, {
+ key: link.id,
class: 'link',
- svgClass: (type ? 'link ' + type : 'link')
+ fromSource: link,
+
+ // functions to aggregate dual link state
+ type: function () {
+ var s = lnk.fromSource,
+ t = lnk.fromTarget;
+ return (s && s.type) || (t && t.type) || defaultLinkType;
+ },
+ online: function () {
+ var s = lnk.fromSource,
+ t = lnk.fromTarget;
+ return (s && s.online) || (t && t.online);
+ },
+ linkWidth: function () {
+ var s = lnk.fromSource,
+ t = lnk.fromTarget,
+ ws = (s && s.linkWidth) || 0,
+ wt = (t && t.linkWidth) || 0;
+ return Math.max(ws, wt);
+ }
});
return lnk;
}
@@ -836,17 +984,9 @@
.range([widthRatio, 12 * widthRatio])
.clamp(true);
- function updateLinkWidth (d) {
- // TODO: watch out for .showPath/.showTraffic classes
- d.el.transition()
- .duration(1000)
- .attr('stroke-width', linkScale(d.linkWidth));
- }
-
-
function updateLinks() {
link = linkG.selectAll('.link')
- .data(network.links, function (d) { return d.id; });
+ .data(network.links, function (d) { return d.key; });
// operate on existing links, if necessary
// link .foo() .bar() ...
@@ -855,19 +995,12 @@
var entering = link.enter()
.append('line')
.attr({
- class: function (d) { return d.svgClass; },
x1: function (d) { return d.x1; },
y1: function (d) { return d.y1; },
x2: function (d) { return d.x2; },
y2: function (d) { return d.y2; },
stroke: config.topo.linkInColor,
'stroke-width': config.topo.linkInWidth
- })
- .classed('inactive', function(d) { return !d.online; })
- .transition().duration(1000)
- .attr({
- 'stroke-width': function (d) { return linkScale(d.linkWidth); },
- stroke: '#666' // TODO: remove explicit stroke, rather...
});
// augment links
@@ -875,6 +1008,7 @@
var link = d3.select(this);
// provide ref to element selection from backing data....
d.el = link;
+ restyleLinkElement(d);
// TODO: add src/dst port labels etc.
});
@@ -1240,9 +1374,9 @@
// TODO: device node exits
}
- function find(id, array) {
+ function find(key, array) {
for (var idx = 0, n = array.length; idx < n; idx++) {
- if (array[idx].id === id) {
+ if (array[idx].key === key) {
return idx;
}
}
@@ -1250,14 +1384,16 @@
}
function removeLinkElement(linkData) {
- // remove from lookup cache
- delete network.lookup[linkData.id];
- // remove from links array
- var idx = find(linkData.id, network.links);
- network.links.splice(idx, 1);
- // remove from SVG
- updateLinks();
- network.force.resume();
+ var idx = find(linkData.key, network.links),
+ removed;
+ if (idx >=0) {
+ // remove from links array
+ removed = network.links.splice(idx, 1);
+ // remove from lookup cache
+ delete network.lookup[removed[0].key];
+ updateLinks();
+ network.force.resume();
+ }
}
function removeHostElement(hostData) {