Simon Hunt

GUI -- Completed ONOS Instance panel, and handling of updateInstance and removeInstance.

- minor refactorings and other cleanup.

Change-Id: I4d0e467f71269f7fb91175e78d2c6af750bf9aad
......@@ -20,6 +20,10 @@
@author Simon Hunt
*/
#feedback {
z-index: 1400;
}
#feedback svg {
position: absolute;
bottom: 0;
......
{
"event": "updateInstance",
"payload": {
"id": "onos-slave",
"ip": "192.168.24.11",
"online": true,
"uiAttached": false,
"switches": 103,
"labels": [
"onos-slave",
"192.168.24.11"
]
}
}
{
"event": "removeInstance",
"payload": {
"id": "onos-leader",
"ip": "192.168.0.5",
"online": false,
"uiAttached": false,
"switches": 0,
"labels": [
"onos-leader",
"192.168.0.5"
]
}
}
......@@ -20,6 +20,10 @@
@author Simon Hunt
*/
#keymap {
z-index: 1300;
}
#keymap svg {
position: absolute;
bottom: 40px;
......
......@@ -49,7 +49,7 @@ div.onosView.currentView {
#alerts {
display: none;
position: absolute;
z-index: 2000;
z-index: 1200;
opacity: 0.65;
background-color: #006;
color: white;
......@@ -83,10 +83,12 @@ div.onosView.currentView {
color: #66d;
}
#floatPanels {
z-index: 1100;
}
#flyout {
position: absolute;
z-index: 100;
display: block;
top: 10%;
width: 280px;
......
......@@ -785,8 +785,14 @@
function pxHide() {
return (-20 - widthVal()) + 'px';
}
function noPx(what) {
return el.style(what).replace(/px$/, '');
}
function widthVal() {
return el.style('width').replace(/px$/, '');
return noPx('width');
}
function heightVal() {
return noPx('height');
}
fp = {
......@@ -822,6 +828,12 @@
return widthVal();
}
el.style('width', w + 'px');
},
height: function (h) {
if (h === undefined) {
return heightVal();
}
el.style('height', h + 'px');
}
};
fpanels[id] = fp;
......
......@@ -318,8 +318,9 @@ svg .node.host circle {
#topo-oibox div.onosInst {
display: inline-block;
width: 120px;
height: 100px;
width: 170px;
height: 85px;
cursor: pointer;
}
#topo-oibox svg rect {
......@@ -350,75 +351,31 @@ svg .node.host circle {
fill: #fff;
}
#topo-oibox .onosInst.mastership {
opacity: 0.3;
}
#topo-oibox .onosInst.mastership.affinity {
opacity: 1.0;
}
#topo-oibox .onosInst.mastership.affinity svg rect {
filter: url(#blue-glow);
#topo-oibox svg text {
text-anchor: middle;
fill: #888;
}
/* ------------------------------------------------------ */
/* ------------------------------------------------------ */
/* ------------------------------------------------------ */
#topo-oibox .onosInst_OLD {
position: relative;
width: 88%;
left: 4%;
height: 80px;
margin: 8px 0;
cursor: pointer;
-moz-border-radius: 12px;
border-radius: 12px;
/* theme-related */
color: #444;
background-color: #ccc;
border: 4px solid #aaa;
#topo-oibox .online svg text {
fill: #000;
}
#topo-oibox .onosInst_OLD .onosTitle {
text-align: center;
font-size: 10pt;
margin-top: 6px;
color: #888;
#topo-oibox svg text.instTitle {
font-size: 11pt;
font-weight: bold;
}
#topo-oibox .onosInst_OLD.online .onosTitle {
color: black;
#topo-oibox svg text.instLabel {
font-size: 9pt;
font-style: italic;
}
#topo-oibox .onosInst_OLD svg .glyphIcon {
opacity: 0.5;
fill: black;
stroke: none;
fill-rule: evenodd;
}
#topo-oibox .onosInst_OLD.online svg .glyphIcon {
opacity: 1;
fill: black;
stroke: none;
fill-rule: evenodd;
#topo-oibox .onosInst.mastership {
opacity: 0.3;
}
#topo-oibox .onosInst_OLD.online img {
#topo-oibox .onosInst.mastership.affinity {
opacity: 1.0;
padding: 3px;
}
#topo-oibox .onosInst_OLD img.ui {
opacity: 1;
position: absolute;
top: 3px;
right: 3px;
width: 20px;
height: 20px;
#topo-oibox .onosInst.mastership.affinity svg rect {
filter: url(#blue-glow);
}
......
......@@ -580,7 +580,7 @@
updateLink: updateLink,
updateHost: updateHost,
removeInstance: stillToImplement,
removeInstance: removeInstance,
removeDevice: stillToImplement,
removeLink: removeLink,
removeHost: removeHost,
......@@ -715,6 +715,23 @@
}
// TODO: fold removeX(...) methods into base method - remove dup code
function removeInstance(data) {
evTrace(data);
var inst = data.payload,
id = inst.id,
instData = onosInstances[id];
if (instData) {
var idx = find(id, onosOrder, 'id');
if (idx >= 0) {
onosOrder.splice(idx, 1);
}
delete onosInstances[id];
updateInstances();
} else {
logicError('updateInstance lookup fail. ID = "' + id + '"');
}
}
function removeLink(data) {
evTrace(data);
var link = data.payload,
......@@ -793,8 +810,10 @@
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) {
network.view.alert('Unknown event type: "' + data.event + '"');
......@@ -966,56 +985,62 @@
return true;
}
// TODO: these should be moved out to utility module.
function stripPx(s) {
return s.replace(/px$/,'');
}
function appendUse(svg, ox, oy, dim, iid, cls) {
var use = svg.append('use').attr({
transform: translate(ox,oy),
'xlink:href': iid,
width: dim,
height: dim
});
if (cls) {
use.classed(cls, true);
}
return use;
}
// ==============================
// onos instance panel functions
function appendGlyph(svg, ox, oy, dim, iid, cls) {
appendUse(svg, ox, oy, dim, iid, cls).classed('glyphIcon', true);
}
var instCfg = {
rectPad: 8,
nodeOx: 9,
nodeOy: 9,
nodeDim: 40,
birdOx: 19,
birdOy: 21,
birdDim: 21,
uiDy: 45,
titleDy: 30,
textYOff: 20,
textYSpc: 15
};
function appendBadge(svg, ox, oy, dim, iid, cls) {
appendUse(svg, ox, oy, dim, iid,cls ).classed('badgeIcon', true);
function viewBox(dim) {
return '0 0 ' + dim.w + ' ' + dim.h;
}
function attachUiBadge(svg) {
appendBadge(svg, 12, 50, 30, '#uiAttached', 'uiBadge');
function instRectAttr(dim) {
var pad = instCfg.rectPad;
return {
x: pad,
y: pad,
width: dim.w - pad*2,
height: dim.h - pad*2,
rx: 12
};
}
// ==============================
// onos instance panel functions
var instW = 120;
function viewBox(w, h) {
return '0 0 ' + w + ' ' + h;
function computeDim(self) {
var css = window.getComputedStyle(self);
return {
w: stripPx(css.width),
h: stripPx(css.height)
};
}
function updateInstances() {
var onoses = oiBox.el.selectAll('.onosInst')
.data(onosOrder, function (d) { return d.id; }),
boxW = instW * onosOrder.length;
instDim = {w:0,h:0},
c = instCfg;
// adjust the width of the panel based on number of instances...
oiBox.width(boxW);
function nSw(n) {
return '# Switches: ' + n;
}
// operate on existing onos instances if necessary
onoses.each(function (d) {
var el = d3.select(this),
svg = el.select('svg');
instDim = computeDim(this);
// update online state
el.classed('online', d.online);
......@@ -1026,7 +1051,12 @@
attachUiBadge(svg);
}
// TODO: update title and property values
function updAttr(id, value) {
svg.select('text.instLabel.'+id).text(value);
}
updAttr('ip', d.ip);
updAttr('ns', nSw(d.switches));
});
......@@ -1039,61 +1069,64 @@
entering.each(function (d) {
var el = d3.select(this),
css = window.getComputedStyle(this),
w = stripPx(css.width),
h = stripPx(css.height);
var svg = el.append('svg').attr({
width: w,
height: h,
viewBox: viewBox(w, h)
rectAttr,
svg;
instDim = computeDim(this);
rectAttr = instRectAttr(instDim);
svg = el.append('svg').attr({
width: instDim.w,
height: instDim.h,
viewBox: viewBox(instDim)
});
svg.append('rect')
.attr({
x: 8,
y: 8,
width: 104,
height: 84,
rx: 12
});
svg.append('rect').attr(rectAttr);
appendGlyph(svg, 9, 9, 36, '#node');
appendBadge(svg, 17, 19, 21, '#bird');
appendGlyph(svg, c.nodeOx, c.nodeOy, c.nodeDim, '#node');
appendBadge(svg, c.birdOx, c.birdOy, c.birdDim, '#bird');
if (d.uiAttached) {
attachUiBadge(svg);
}
//svg.append('use')
// .attr({
// class: 'birdBadge',
// transform: translate(8,10),
// 'xlink:href': '#bird',
// width: 18,
// height: 18,
// fill: '#fff'
// });
//
//$('<div>').attr('class', 'onosTitle').text(d.id).appendTo(el);
// is the UI attached to this instance?
// TODO: need uiAttached boolean in instance data
// TODO: use SVG glyph, not png..
//if (d.uiAttached) {
//if (i === 0) {
// $('<img src="img/ui.png">').attr('class','ui').appendTo(el);
//}
var left = c.nodeOx + c.nodeDim,
len = rectAttr.width - left,
hlen = len / 2,
midline = hlen + left;
// title
svg.append('text')
.attr({
class: 'instTitle',
x: midline,
y: c.titleDy
})
.text(d.id);
// a couple of attributes
var ty = c.titleDy + c.textYOff;
function addAttr(id, label) {
svg.append('text').attr({
class: 'instLabel ' + id,
x: midline,
y: ty
}).text(label);
ty += c.textYSpc;
}
addAttr('ip', d.ip);
addAttr('ns', nSw(d.switches));
});
// operate on existing + new onoses here
// the departed...
var exiting = onoses.exit()
.transition()
.style('opacity', 0)
.remove();
// adjust the panel size appropriately...
oiBox.width(instDim.w * onosOrder.length);
oiBox.height(instDim.h);
// remove any outgoing instances
onoses.exit().remove();
}
function clickInst(d) {
......@@ -1128,6 +1161,36 @@
oiShowMaster = false;
}
// TODO: these should be moved out to utility module.
function stripPx(s) {
return s.replace(/px$/,'');
}
function appendUse(svg, ox, oy, dim, iid, cls) {
var use = svg.append('use').attr({
transform: translate(ox,oy),
'xlink:href': iid,
width: dim,
height: dim
});
if (cls) {
use.classed(cls, true);
}
return use;
}
function appendGlyph(svg, ox, oy, dim, iid, cls) {
appendUse(svg, ox, oy, dim, iid, cls).classed('glyphIcon', true);
}
function appendBadge(svg, ox, oy, dim, iid, cls) {
appendUse(svg, ox, oy, dim, iid,cls ).classed('badgeIcon', true);
}
function attachUiBadge(svg) {
appendBadge(svg, 12, instCfg.uiDy, 30, '#uiAttached', 'uiBadge');
}
// ==============================
// force layout modification functions
......@@ -1763,9 +1826,12 @@
}
function find(key, array) {
for (var idx = 0, n = array.length; idx < n; idx++) {
if (array[idx].key === key) {
function find(key, array, tag) {
var _tag = tag || 'key',
idx, n, d;
for (idx = 0, n = array.length; idx < n; idx++) {
d = array[idx];
if (d[_tag] === key) {
return idx;
}
}
......