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
This commit is contained in:
Simon Hunt 2014-11-28 18:07:35 -08:00
parent 9c57bddf00
commit ca867ac786
7 changed files with 224 additions and 69 deletions

View File

@ -28,8 +28,8 @@
"labels": [""] "labels": [""]
}, },
{ {
"class": "primary optical", "class": "animated optical",
"traffic": false, "traffic": true,
"links": [ "links": [
"of:0000ffffffff0008/4-of:0000ffffffffff08/1" "of:0000ffffffff0008/4-of:0000ffffffffff08/1"
], ],
@ -44,8 +44,8 @@
"labels": [""] "labels": [""]
}, },
{ {
"class": "animated optical", "class": "primary optical",
"traffic": true, "traffic": false,
"links": [ "links": [
"of:0000ffffffffff08/4-of:0000ffffffffff03/1" "of:0000ffffffffff08/4-of:0000ffffffffff03/1"
], ],

View File

@ -0,0 +1,17 @@
{
"event": "updateDevice",
"payload": {
"id": "of:0000ffffffff0007",
"type": "switch",
"online": false,
"labels": [
"",
"sw-7",
"0000ffffffff0007"
],
"metaUi": {
"x": 530,
"y": 330
}
}
}

View File

@ -0,0 +1,17 @@
{
"event": "updateDevice",
"payload": {
"id": "of:0000ffffffff0007",
"type": "switch",
"online": true,
"labels": [
"",
"sw-7",
"0000ffffffff0007"
],
"metaUi": {
"x": 530,
"y": 330
}
}
}

View File

@ -0,0 +1,17 @@
{
"event": "removeDevice",
"payload": {
"id": "of:0000ffffffff0008",
"type": "switch",
"online": false,
"labels": [
"",
"sw-8",
"0000ffffffff0008"
],
"metaUi": {
"x": 734,
"y": 477
}
}
}

View File

@ -0,0 +1,17 @@
{
"event": "addDevice",
"payload": {
"id": "of:0000ffffffff0008",
"type": "switch",
"online": true,
"labels": [
"",
"sw-8",
"0000ffffffff0008"
],
"metaUi": {
"x": 734,
"y": 477
}
}
}

View File

@ -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": {}
}
}

View File

@ -596,7 +596,7 @@
updateHost: updateHost, updateHost: updateHost,
removeInstance: removeInstance, removeInstance: removeInstance,
removeDevice: stillToImplement, removeDevice: removeDevice,
removeLink: removeLink, removeLink: removeLink,
removeHost: removeHost, removeHost: removeHost,
@ -621,9 +621,17 @@
function addDevice(data) { function addDevice(data) {
evTrace(data); evTrace(data);
var device = data.payload, var device = data.payload,
nodeData = createDeviceNode(device); id = device.id,
network.nodes.push(nodeData); d;
network.lookup[nodeData.id] = nodeData;
if (network.lookup[id]) {
logicError('Device already added: ' + id);
return;
}
d = createDeviceNode(device);
network.nodes.push(d);
network.lookup[id] = d;
updateNodes(); updateNodes();
network.force.start(); network.force.start();
} }
@ -633,24 +641,24 @@
var link = data.payload, var link = data.payload,
result = findLink(link, 'add'), result = findLink(link, 'add'),
bad = result.badLogic, bad = result.badLogic,
ldata = result.ldata; d = result.ldata;
if (bad) { if (bad) {
logicError(bad + ': ' + link.id); logicError(bad + ': ' + link.id);
return; return;
} }
if (ldata) { if (d) {
// we already have a backing store link for src/dst nodes // we already have a backing store link for src/dst nodes
addLinkUpdate(ldata, link); addLinkUpdate(d, link);
return; return;
} }
// no backing store link yet // no backing store link yet
ldata = createLink(link); d = createLink(link);
if (ldata) { if (d) {
network.links.push(ldata); network.links.push(d);
network.lookup[ldata.key] = ldata; network.lookup[d.key] = d;
updateLinks(); updateLinks();
network.force.start(); network.force.start();
} }
@ -659,18 +667,26 @@
function addHost(data) { function addHost(data) {
evTrace(data); evTrace(data);
var host = data.payload, var host = data.payload,
node = createHostNode(host), id = host.id,
d,
lnk; 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(); updateNodes();
lnk = createHostLink(host); lnk = createHostLink(host);
if (lnk) { if (lnk) {
node.linkData = lnk; // cache ref on its host d.linkData = lnk; // cache ref on its host
network.links.push(lnk); network.links.push(lnk);
network.lookup[host.ingress] = lnk; network.lookup[d.ingress] = lnk;
network.lookup[host.egress] = lnk; network.lookup[d.egress] = lnk;
updateLinks(); updateLinks();
} }
network.force.start(); network.force.start();
@ -682,9 +698,9 @@
evTrace(data); evTrace(data);
var inst = data.payload, var inst = data.payload,
id = inst.id, id = inst.id,
instData = onosInstances[id]; d = onosInstances[id];
if (instData) { if (d) {
$.extend(instData, inst); $.extend(d, inst);
updateInstances(); updateInstances();
} else { } else {
logicError('updateInstance lookup fail. ID = "' + id + '"'); logicError('updateInstance lookup fail. ID = "' + id + '"');
@ -723,10 +739,10 @@
evTrace(data); evTrace(data);
var host = data.payload, var host = data.payload,
id = host.id, id = host.id,
hostData = network.lookup[id]; d = network.lookup[id];
if (hostData) { if (d) {
$.extend(hostData, host); $.extend(d, host);
updateHostState(hostData); updateHostState(d);
} else { } else {
logicError('updateHost lookup fail. ID = "' + id + '"'); logicError('updateHost lookup fail. ID = "' + id + '"');
} }
@ -737,9 +753,9 @@
evTrace(data); evTrace(data);
var inst = data.payload, var inst = data.payload,
id = inst.id, id = inst.id,
instData = onosInstances[id]; d = onosInstances[id];
if (instData) { if (d) {
var idx = find(id, onosOrder, 'id'); var idx = find(id, onosOrder);
if (idx >= 0) { if (idx >= 0) {
onosOrder.splice(idx, 1); 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) { function removeLink(data) {
evTrace(data); evTrace(data);
var link = data.payload, var link = data.payload,
result = findLink(link, 'remove'), result = findLink(link, 'remove'),
bad = result.badLogic; bad = result.badLogic;
if (bad) { if (bad) {
logicError(bad + ': ' + link.id); // may have already removed link, if attached to removed device
console.warn(bad + ': ' + link.id);
return; return;
} }
result.removeRawLink(); result.removeRawLink();
@ -766,14 +795,16 @@
evTrace(data); evTrace(data);
var host = data.payload, var host = data.payload,
id = host.id, id = host.id,
hostData = network.lookup[id]; d = network.lookup[id];
if (hostData) { if (d) {
removeHostElement(hostData); removeHostElement(d, true);
} else { } 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) { function showSummary(data) {
evTrace(data); evTrace(data);
populateSummary(data.payload); 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) { function unknownEvent(data) {
console.warn('Unknown event type: "' + data.event + '"', data); console.warn('Unknown event type: "' + data.event + '"', data);
} }
@ -854,17 +877,6 @@
function getSel(idx) { function getSel(idx) {
return selections[selectOrder[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) { function allSelectionsClass(cls) {
for (var i=0, n=nSel(); i<n; i++) { for (var i=0, n=nSel(); i<n; i++) {
if (getSel(i).obj.class !== cls) { if (getSel(i).obj.class !== cls) {
@ -895,7 +907,6 @@
updateDeviceColors(); updateDeviceColors();
} }
function toggleSummary() { function toggleSummary() {
if (!summaryPane.isVisible()) { if (!summaryPane.isVisible()) {
requestSummary(); requestSummary();
@ -1841,14 +1852,18 @@
// host node exits.... // host node exits....
exiting.filter('.host').each(function (d) { 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') node.select('text')
.style('opacity', 0.5) .style('opacity', 0.5)
.transition() .transition()
.duration(1000) .duration(800)
.style('opacity', 0); .style('opacity', 0);
// note, leave <g>.remove to remove this element
node.select('circle') node.select('circle')
.style('stroke-fill', '#555') .style('stroke-fill', '#555')
@ -1857,11 +1872,22 @@
.transition() .transition()
.duration(1500) .duration(1500)
.attr('r', 0); .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(); network.force.resume();
} }
@ -1968,7 +1994,7 @@
} }
function find(key, array, tag) { function find(key, array, tag) {
var _tag = tag || 'key', var _tag = tag || 'id',
idx, n, d; idx, n, d;
for (idx = 0, n = array.length; idx < n; idx++) { for (idx = 0, n = array.length; idx < n; idx++) {
d = array[idx]; d = array[idx];
@ -1979,8 +2005,8 @@
return -1; return -1;
} }
function removeLinkElement(linkData) { function removeLinkElement(d) {
var idx = find(linkData.key, network.links), var idx = find(d.key, network.links, 'key'),
removed; removed;
if (idx >=0) { if (idx >=0) {
// remove from links array // remove from links array
@ -1992,20 +2018,64 @@
} }
} }
function removeHostElement(hostData) { function removeHostElement(d, upd) {
var lu = network.lookup;
// first, remove associated hostLink... // 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 // remove from lookup cache
delete network.lookup[hostData.id]; delete lu[d.id];
// remove from nodes array // 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); network.nodes.splice(idx, 1);
// remove from SVG // remove from SVG
updateNodes(); updateNodes();
network.force.resume(); 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() { function tick() {
node.attr({ node.attr({