GUI -- [ONOS-310] - Implemented removeDevice().
- when a device is removed, we also removed any attached hosts and links.
- cleaned up animations, etc.
Change-Id: Ifc82f7f60dd8c7bbe4d32beeb923969713492430
diff --git a/web/gui/src/main/webapp/json/ev/traffic/ev_18_onos.json b/web/gui/src/main/webapp/json/ev/traffic/ev_18_onos.json
index 7ecaa88..3b3e921 100644
--- a/web/gui/src/main/webapp/json/ev/traffic/ev_18_onos.json
+++ b/web/gui/src/main/webapp/json/ev/traffic/ev_18_onos.json
@@ -28,8 +28,8 @@
"labels": [""]
},
{
- "class": "primary optical",
- "traffic": false,
+ "class": "animated optical",
+ "traffic": true,
"links": [
"of:0000ffffffff0008/4-of:0000ffffffffff08/1"
],
@@ -44,8 +44,8 @@
"labels": [""]
},
{
- "class": "animated optical",
- "traffic": true,
+ "class": "primary optical",
+ "traffic": false,
"links": [
"of:0000ffffffffff08/4-of:0000ffffffffff03/1"
],
diff --git a/web/gui/src/main/webapp/json/ev/traffic/ev_20_onos.json b/web/gui/src/main/webapp/json/ev/traffic/ev_20_onos.json
new file mode 100644
index 0000000..2f9d567
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/traffic/ev_20_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "updateDevice",
+ "payload": {
+ "id": "of:0000ffffffff0007",
+ "type": "switch",
+ "online": false,
+ "labels": [
+ "",
+ "sw-7",
+ "0000ffffffff0007"
+ ],
+ "metaUi": {
+ "x": 530,
+ "y": 330
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/traffic/ev_21_onos.json b/web/gui/src/main/webapp/json/ev/traffic/ev_21_onos.json
new file mode 100644
index 0000000..a409b61
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/traffic/ev_21_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "updateDevice",
+ "payload": {
+ "id": "of:0000ffffffff0007",
+ "type": "switch",
+ "online": true,
+ "labels": [
+ "",
+ "sw-7",
+ "0000ffffffff0007"
+ ],
+ "metaUi": {
+ "x": 530,
+ "y": 330
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/traffic/ev_22_onos.json b/web/gui/src/main/webapp/json/ev/traffic/ev_22_onos.json
new file mode 100644
index 0000000..0478da1
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/traffic/ev_22_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "removeDevice",
+ "payload": {
+ "id": "of:0000ffffffff0008",
+ "type": "switch",
+ "online": false,
+ "labels": [
+ "",
+ "sw-8",
+ "0000ffffffff0008"
+ ],
+ "metaUi": {
+ "x": 734,
+ "y": 477
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/traffic/ev_23_onos.json b/web/gui/src/main/webapp/json/ev/traffic/ev_23_onos.json
new file mode 100644
index 0000000..0a4c853
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/traffic/ev_23_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000ffffffff0008",
+ "type": "switch",
+ "online": true,
+ "labels": [
+ "",
+ "sw-8",
+ "0000ffffffff0008"
+ ],
+ "metaUi": {
+ "x": 734,
+ "y": 477
+ }
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/traffic/ev_24_onos.json b/web/gui/src/main/webapp/json/ev/traffic/ev_24_onos.json
new file mode 100644
index 0000000..69fc2bc
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/traffic/ev_24_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "removeHost",
+ "payload": {
+ "id": "0E:2A:69:30:13:88/-1",
+ "ingress": "0E:2A:69:30:13:88/-1/0-of:0000ffffffff0007/101",
+ "egress": "of:0000ffffffff0007/101-0E:2A:69:30:13:86/-1/0",
+ "cp": {
+ "device": "of:0000ffffffff0007",
+ "port": 101
+ },
+ "labels": [
+ "4.5.7.6",
+ "0E:2A:69:30:13:88"
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/topo.js b/web/gui/src/main/webapp/topo.js
index e233f8c..5b46e3d 100644
--- a/web/gui/src/main/webapp/topo.js
+++ b/web/gui/src/main/webapp/topo.js
@@ -596,7 +596,7 @@
updateHost: updateHost,
removeInstance: removeInstance,
- removeDevice: stillToImplement,
+ removeDevice: removeDevice,
removeLink: removeLink,
removeHost: removeHost,
@@ -621,9 +621,17 @@
function addDevice(data) {
evTrace(data);
var device = data.payload,
- nodeData = createDeviceNode(device);
- network.nodes.push(nodeData);
- network.lookup[nodeData.id] = nodeData;
+ id = device.id,
+ d;
+
+ if (network.lookup[id]) {
+ logicError('Device already added: ' + id);
+ return;
+ }
+
+ d = createDeviceNode(device);
+ network.nodes.push(d);
+ network.lookup[id] = d;
updateNodes();
network.force.start();
}
@@ -633,24 +641,24 @@
var link = data.payload,
result = findLink(link, 'add'),
bad = result.badLogic,
- ldata = result.ldata;
+ d = result.ldata;
if (bad) {
logicError(bad + ': ' + link.id);
return;
}
- if (ldata) {
+ if (d) {
// we already have a backing store link for src/dst nodes
- addLinkUpdate(ldata, link);
+ addLinkUpdate(d, link);
return;
}
// no backing store link yet
- ldata = createLink(link);
- if (ldata) {
- network.links.push(ldata);
- network.lookup[ldata.key] = ldata;
+ d = createLink(link);
+ if (d) {
+ network.links.push(d);
+ network.lookup[d.key] = d;
updateLinks();
network.force.start();
}
@@ -659,18 +667,26 @@
function addHost(data) {
evTrace(data);
var host = data.payload,
- node = createHostNode(host),
+ id = host.id,
+ d,
lnk;
- network.nodes.push(node);
- network.lookup[host.id] = node;
+
+ if (network.lookup[id]) {
+ logicError('Host already added: ' + id);
+ return;
+ }
+
+ d = createHostNode(host);
+ network.nodes.push(d);
+ network.lookup[host.id] = d;
updateNodes();
lnk = createHostLink(host);
if (lnk) {
- node.linkData = lnk; // cache ref on its host
+ d.linkData = lnk; // cache ref on its host
network.links.push(lnk);
- network.lookup[host.ingress] = lnk;
- network.lookup[host.egress] = lnk;
+ network.lookup[d.ingress] = lnk;
+ network.lookup[d.egress] = lnk;
updateLinks();
}
network.force.start();
@@ -682,9 +698,9 @@
evTrace(data);
var inst = data.payload,
id = inst.id,
- instData = onosInstances[id];
- if (instData) {
- $.extend(instData, inst);
+ d = onosInstances[id];
+ if (d) {
+ $.extend(d, inst);
updateInstances();
} else {
logicError('updateInstance lookup fail. ID = "' + id + '"');
@@ -723,10 +739,10 @@
evTrace(data);
var host = data.payload,
id = host.id,
- hostData = network.lookup[id];
- if (hostData) {
- $.extend(hostData, host);
- updateHostState(hostData);
+ d = network.lookup[id];
+ if (d) {
+ $.extend(d, host);
+ updateHostState(d);
} else {
logicError('updateHost lookup fail. ID = "' + id + '"');
}
@@ -737,9 +753,9 @@
evTrace(data);
var inst = data.payload,
id = inst.id,
- instData = onosInstances[id];
- if (instData) {
- var idx = find(id, onosOrder, 'id');
+ d = onosInstances[id];
+ if (d) {
+ var idx = find(id, onosOrder);
if (idx >= 0) {
onosOrder.splice(idx, 1);
}
@@ -750,13 +766,26 @@
}
}
+ function removeDevice(data) {
+ evTrace(data);
+ var device = data.payload,
+ id = device.id,
+ d = network.lookup[id];
+ if (d) {
+ removeDeviceElement(d);
+ } else {
+ logicError('removeDevice lookup fail. ID = "' + id + '"');
+ }
+ }
+
function removeLink(data) {
evTrace(data);
var link = data.payload,
result = findLink(link, 'remove'),
bad = result.badLogic;
if (bad) {
- logicError(bad + ': ' + link.id);
+ // may have already removed link, if attached to removed device
+ console.warn(bad + ': ' + link.id);
return;
}
result.removeRawLink();
@@ -766,14 +795,16 @@
evTrace(data);
var host = data.payload,
id = host.id,
- hostData = network.lookup[id];
- if (hostData) {
- removeHostElement(hostData);
+ d = network.lookup[id];
+ if (d) {
+ removeHostElement(d, true);
} else {
- logicError('removeHost lookup fail. ID = "' + id + '"');
+ // may have already removed host, if attached to removed device
+ console.warn('removeHost lookup fail. ID = "' + id + '"');
}
}
+ // the following events are server responses to user actions
function showSummary(data) {
evTrace(data);
populateSummary(data.payload);
@@ -828,14 +859,6 @@
// ...............................
- function stillToImplement(data) {
- var p = data.payload;
- note(data.event, p.id);
- if (!config.useLiveData) {
- network.view.alert('Not yet implemented: "' + data.event + '"');
- }
- }
-
function unknownEvent(data) {
console.warn('Unknown event type: "' + data.event + '"', data);
}
@@ -854,17 +877,6 @@
function getSel(idx) {
return selections[selectOrder[idx]];
}
- function getSelId(idx) {
- return getSel(idx).obj.id;
- }
- function getSelIds(start, endOffset) {
- var end = selectOrder.length - endOffset;
- var ids = [];
- selectOrder.slice(start, end).forEach(function (d) {
- ids.push(getSelId(d));
- });
- return ids;
- }
function allSelectionsClass(cls) {
for (var i=0, n=nSel(); i<n; i++) {
if (getSel(i).obj.class !== cls) {
@@ -895,7 +907,6 @@
updateDeviceColors();
}
-
function toggleSummary() {
if (!summaryPane.isVisible()) {
requestSummary();
@@ -1841,14 +1852,18 @@
// host node exits....
exiting.filter('.host').each(function (d) {
- var node = d3.select(this);
+ var node = d.el;
+ node.select('use')
+ .style('opacity', 0.5)
+ .transition()
+ .duration(800)
+ .style('opacity', 0);
node.select('text')
.style('opacity', 0.5)
.transition()
- .duration(1000)
+ .duration(800)
.style('opacity', 0);
- // note, leave <g>.remove to remove this element
node.select('circle')
.style('stroke-fill', '#555')
@@ -1857,11 +1872,22 @@
.transition()
.duration(1500)
.attr('r', 0);
- // note, leave <g>.remove to remove this element
-
});
- // TODO: device node exit animation
+ // device node exits....
+ exiting.filter('.device').each(function (d) {
+ var node = d.el;
+ node.select('use')
+ .style('opacity', 0.5)
+ .transition()
+ .duration(800)
+ .style('opacity', 0);
+
+ node.selectAll('rect')
+ .style('stroke-fill', '#555')
+ .style('fill', '#888')
+ .style('opacity', 0.5);
+ });
network.force.resume();
}
@@ -1968,7 +1994,7 @@
}
function find(key, array, tag) {
- var _tag = tag || 'key',
+ var _tag = tag || 'id',
idx, n, d;
for (idx = 0, n = array.length; idx < n; idx++) {
d = array[idx];
@@ -1979,8 +2005,8 @@
return -1;
}
- function removeLinkElement(linkData) {
- var idx = find(linkData.key, network.links),
+ function removeLinkElement(d) {
+ var idx = find(d.key, network.links, 'key'),
removed;
if (idx >=0) {
// remove from links array
@@ -1992,20 +2018,64 @@
}
}
- function removeHostElement(hostData) {
+ function removeHostElement(d, upd) {
+ var lu = network.lookup;
// first, remove associated hostLink...
- removeLinkElement(hostData.linkData);
+ removeLinkElement(d.linkData);
+
+ // remove hostLink bindings
+ delete lu[d.ingress];
+ delete lu[d.egress];
// remove from lookup cache
- delete network.lookup[hostData.id];
+ delete lu[d.id];
// remove from nodes array
- var idx = find(hostData.id, network.nodes);
+ var idx = find(d.id, network.nodes);
+ network.nodes.splice(idx, 1);
+ // remove from SVG
+ // NOTE: upd is false if we were called from removeDeviceElement()
+ if (upd) {
+ updateNodes();
+ network.force.resume();
+ }
+ }
+
+
+ function removeDeviceElement(d) {
+ var id = d.id;
+ // first, remove associated hosts and links..
+ findAttachedHosts(id).forEach(removeHostElement);
+ findAttachedLinks(id).forEach(removeLinkElement);
+
+ // remove from lookup cache
+ delete network.lookup[id];
+ // remove from nodes array
+ var idx = find(id, network.nodes);
network.nodes.splice(idx, 1);
// remove from SVG
updateNodes();
network.force.resume();
}
+ function findAttachedHosts(devId) {
+ var hosts = [];
+ network.nodes.forEach(function (d) {
+ if (d.class === 'host' && d.cp.device === devId) {
+ hosts.push(d);
+ }
+ });
+ return hosts;
+ }
+
+ function findAttachedLinks(devId) {
+ var links = [];
+ network.links.forEach(function (d) {
+ if (d.source.id === devId || d.target.id === devId) {
+ links.push(d);
+ }
+ });
+ return links;
+ }
function tick() {
node.attr({