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 @@ ...@@ -20,6 +20,10 @@
20 @author Simon Hunt 20 @author Simon Hunt
21 */ 21 */
22 22
23 +#feedback {
24 + z-index: 1400;
25 +}
26 +
23 #feedback svg { 27 #feedback svg {
24 position: absolute; 28 position: absolute;
25 bottom: 0; 29 bottom: 0;
......
1 +{
2 + "event": "updateInstance",
3 + "payload": {
4 + "id": "onos-slave",
5 + "ip": "192.168.24.11",
6 + "online": true,
7 + "uiAttached": false,
8 + "switches": 103,
9 + "labels": [
10 + "onos-slave",
11 + "192.168.24.11"
12 + ]
13 + }
14 +}
1 +{
2 + "event": "removeInstance",
3 + "payload": {
4 + "id": "onos-leader",
5 + "ip": "192.168.0.5",
6 + "online": false,
7 + "uiAttached": false,
8 + "switches": 0,
9 + "labels": [
10 + "onos-leader",
11 + "192.168.0.5"
12 + ]
13 + }
14 +}
...@@ -20,6 +20,10 @@ ...@@ -20,6 +20,10 @@
20 @author Simon Hunt 20 @author Simon Hunt
21 */ 21 */
22 22
23 +#keymap {
24 + z-index: 1300;
25 +}
26 +
23 #keymap svg { 27 #keymap svg {
24 position: absolute; 28 position: absolute;
25 bottom: 40px; 29 bottom: 40px;
......
...@@ -49,7 +49,7 @@ div.onosView.currentView { ...@@ -49,7 +49,7 @@ div.onosView.currentView {
49 #alerts { 49 #alerts {
50 display: none; 50 display: none;
51 position: absolute; 51 position: absolute;
52 - z-index: 2000; 52 + z-index: 1200;
53 opacity: 0.65; 53 opacity: 0.65;
54 background-color: #006; 54 background-color: #006;
55 color: white; 55 color: white;
...@@ -83,10 +83,12 @@ div.onosView.currentView { ...@@ -83,10 +83,12 @@ div.onosView.currentView {
83 color: #66d; 83 color: #66d;
84 } 84 }
85 85
86 +#floatPanels {
87 + z-index: 1100;
88 +}
86 89
87 #flyout { 90 #flyout {
88 position: absolute; 91 position: absolute;
89 - z-index: 100;
90 display: block; 92 display: block;
91 top: 10%; 93 top: 10%;
92 width: 280px; 94 width: 280px;
......
...@@ -785,8 +785,14 @@ ...@@ -785,8 +785,14 @@
785 function pxHide() { 785 function pxHide() {
786 return (-20 - widthVal()) + 'px'; 786 return (-20 - widthVal()) + 'px';
787 } 787 }
788 + function noPx(what) {
789 + return el.style(what).replace(/px$/, '');
790 + }
788 function widthVal() { 791 function widthVal() {
789 - return el.style('width').replace(/px$/, ''); 792 + return noPx('width');
793 + }
794 + function heightVal() {
795 + return noPx('height');
790 } 796 }
791 797
792 fp = { 798 fp = {
...@@ -822,6 +828,12 @@ ...@@ -822,6 +828,12 @@
822 return widthVal(); 828 return widthVal();
823 } 829 }
824 el.style('width', w + 'px'); 830 el.style('width', w + 'px');
831 + },
832 + height: function (h) {
833 + if (h === undefined) {
834 + return heightVal();
835 + }
836 + el.style('height', h + 'px');
825 } 837 }
826 }; 838 };
827 fpanels[id] = fp; 839 fpanels[id] = fp;
......
...@@ -318,8 +318,9 @@ svg .node.host circle { ...@@ -318,8 +318,9 @@ svg .node.host circle {
318 318
319 #topo-oibox div.onosInst { 319 #topo-oibox div.onosInst {
320 display: inline-block; 320 display: inline-block;
321 - width: 120px; 321 + width: 170px;
322 - height: 100px; 322 + height: 85px;
323 + cursor: pointer;
323 } 324 }
324 325
325 #topo-oibox svg rect { 326 #topo-oibox svg rect {
...@@ -350,75 +351,31 @@ svg .node.host circle { ...@@ -350,75 +351,31 @@ svg .node.host circle {
350 fill: #fff; 351 fill: #fff;
351 } 352 }
352 353
353 - 354 +#topo-oibox svg text {
354 -#topo-oibox .onosInst.mastership { 355 + text-anchor: middle;
355 - opacity: 0.3; 356 + fill: #888;
356 -}
357 -#topo-oibox .onosInst.mastership.affinity {
358 - opacity: 1.0;
359 -}
360 -#topo-oibox .onosInst.mastership.affinity svg rect {
361 - filter: url(#blue-glow);
362 } 357 }
363 - 358 +#topo-oibox .online svg text {
364 -/* ------------------------------------------------------ */ 359 + fill: #000;
365 -/* ------------------------------------------------------ */
366 -/* ------------------------------------------------------ */
367 -
368 -#topo-oibox .onosInst_OLD {
369 - position: relative;
370 - width: 88%;
371 - left: 4%;
372 - height: 80px;
373 - margin: 8px 0;
374 - cursor: pointer;
375 -
376 - -moz-border-radius: 12px;
377 - border-radius: 12px;
378 -
379 - /* theme-related */
380 - color: #444;
381 - background-color: #ccc;
382 - border: 4px solid #aaa;
383 } 360 }
384 361
385 -#topo-oibox .onosInst_OLD .onosTitle { 362 +#topo-oibox svg text.instTitle {
386 - text-align: center; 363 + font-size: 11pt;
387 - font-size: 10pt; 364 + font-weight: bold;
388 - margin-top: 6px;
389 - color: #888;
390 } 365 }
391 - 366 +#topo-oibox svg text.instLabel {
392 -#topo-oibox .onosInst_OLD.online .onosTitle { 367 + font-size: 9pt;
393 - color: black; 368 + font-style: italic;
394 } 369 }
395 370
396 -#topo-oibox .onosInst_OLD svg .glyphIcon { 371 +#topo-oibox .onosInst.mastership {
397 - opacity: 0.5; 372 + opacity: 0.3;
398 - fill: black;
399 - stroke: none;
400 - fill-rule: evenodd;
401 -}
402 -#topo-oibox .onosInst_OLD.online svg .glyphIcon {
403 - opacity: 1;
404 - fill: black;
405 - stroke: none;
406 - fill-rule: evenodd;
407 } 373 }
408 - 374 +#topo-oibox .onosInst.mastership.affinity {
409 -
410 -#topo-oibox .onosInst_OLD.online img {
411 opacity: 1.0; 375 opacity: 1.0;
412 - padding: 3px;
413 } 376 }
414 - 377 +#topo-oibox .onosInst.mastership.affinity svg rect {
415 -#topo-oibox .onosInst_OLD img.ui { 378 + filter: url(#blue-glow);
416 - opacity: 1;
417 - position: absolute;
418 - top: 3px;
419 - right: 3px;
420 - width: 20px;
421 - height: 20px;
422 } 379 }
423 380
424 381
......
...@@ -580,7 +580,7 @@ ...@@ -580,7 +580,7 @@
580 updateLink: updateLink, 580 updateLink: updateLink,
581 updateHost: updateHost, 581 updateHost: updateHost,
582 582
583 - removeInstance: stillToImplement, 583 + removeInstance: removeInstance,
584 removeDevice: stillToImplement, 584 removeDevice: stillToImplement,
585 removeLink: removeLink, 585 removeLink: removeLink,
586 removeHost: removeHost, 586 removeHost: removeHost,
...@@ -715,6 +715,23 @@ ...@@ -715,6 +715,23 @@
715 } 715 }
716 716
717 // TODO: fold removeX(...) methods into base method - remove dup code 717 // TODO: fold removeX(...) methods into base method - remove dup code
718 + function removeInstance(data) {
719 + evTrace(data);
720 + var inst = data.payload,
721 + id = inst.id,
722 + instData = onosInstances[id];
723 + if (instData) {
724 + var idx = find(id, onosOrder, 'id');
725 + if (idx >= 0) {
726 + onosOrder.splice(idx, 1);
727 + }
728 + delete onosInstances[id];
729 + updateInstances();
730 + } else {
731 + logicError('updateInstance lookup fail. ID = "' + id + '"');
732 + }
733 + }
734 +
718 function removeLink(data) { 735 function removeLink(data) {
719 evTrace(data); 736 evTrace(data);
720 var link = data.payload, 737 var link = data.payload,
...@@ -793,8 +810,10 @@ ...@@ -793,8 +810,10 @@
793 function stillToImplement(data) { 810 function stillToImplement(data) {
794 var p = data.payload; 811 var p = data.payload;
795 note(data.event, p.id); 812 note(data.event, p.id);
813 + if (!config.useLiveData) {
796 network.view.alert('Not yet implemented: "' + data.event + '"'); 814 network.view.alert('Not yet implemented: "' + data.event + '"');
797 } 815 }
816 + }
798 817
799 function unknownEvent(data) { 818 function unknownEvent(data) {
800 network.view.alert('Unknown event type: "' + data.event + '"'); 819 network.view.alert('Unknown event type: "' + data.event + '"');
...@@ -966,56 +985,62 @@ ...@@ -966,56 +985,62 @@
966 return true; 985 return true;
967 } 986 }
968 987
969 - // TODO: these should be moved out to utility module.
970 - function stripPx(s) {
971 - return s.replace(/px$/,'');
972 - }
973 988
974 - function appendUse(svg, ox, oy, dim, iid, cls) { 989 + // ==============================
975 - var use = svg.append('use').attr({ 990 + // onos instance panel functions
976 - transform: translate(ox,oy),
977 - 'xlink:href': iid,
978 - width: dim,
979 - height: dim
980 - });
981 - if (cls) {
982 - use.classed(cls, true);
983 - }
984 - return use;
985 - }
986 991
987 - function appendGlyph(svg, ox, oy, dim, iid, cls) { 992 + var instCfg = {
988 - appendUse(svg, ox, oy, dim, iid, cls).classed('glyphIcon', true); 993 + rectPad: 8,
989 - } 994 + nodeOx: 9,
995 + nodeOy: 9,
996 + nodeDim: 40,
997 + birdOx: 19,
998 + birdOy: 21,
999 + birdDim: 21,
1000 + uiDy: 45,
1001 + titleDy: 30,
1002 + textYOff: 20,
1003 + textYSpc: 15
1004 + };
990 1005
991 - function appendBadge(svg, ox, oy, dim, iid, cls) { 1006 + function viewBox(dim) {
992 - appendUse(svg, ox, oy, dim, iid,cls ).classed('badgeIcon', true); 1007 + return '0 0 ' + dim.w + ' ' + dim.h;
993 } 1008 }
994 1009
995 - function attachUiBadge(svg) { 1010 + function instRectAttr(dim) {
996 - appendBadge(svg, 12, 50, 30, '#uiAttached', 'uiBadge'); 1011 + var pad = instCfg.rectPad;
1012 + return {
1013 + x: pad,
1014 + y: pad,
1015 + width: dim.w - pad*2,
1016 + height: dim.h - pad*2,
1017 + rx: 12
1018 + };
997 } 1019 }
998 1020
999 - // ============================== 1021 + function computeDim(self) {
1000 - // onos instance panel functions 1022 + var css = window.getComputedStyle(self);
1001 - var instW = 120; 1023 + return {
1002 - 1024 + w: stripPx(css.width),
1003 - function viewBox(w, h) { 1025 + h: stripPx(css.height)
1004 - return '0 0 ' + w + ' ' + h; 1026 + };
1005 } 1027 }
1006 1028
1007 function updateInstances() { 1029 function updateInstances() {
1008 var onoses = oiBox.el.selectAll('.onosInst') 1030 var onoses = oiBox.el.selectAll('.onosInst')
1009 .data(onosOrder, function (d) { return d.id; }), 1031 .data(onosOrder, function (d) { return d.id; }),
1010 - boxW = instW * onosOrder.length; 1032 + instDim = {w:0,h:0},
1033 + c = instCfg;
1011 1034
1012 - // adjust the width of the panel based on number of instances... 1035 + function nSw(n) {
1013 - oiBox.width(boxW); 1036 + return '# Switches: ' + n;
1037 + }
1014 1038
1015 // operate on existing onos instances if necessary 1039 // operate on existing onos instances if necessary
1016 onoses.each(function (d) { 1040 onoses.each(function (d) {
1017 var el = d3.select(this), 1041 var el = d3.select(this),
1018 svg = el.select('svg'); 1042 svg = el.select('svg');
1043 + instDim = computeDim(this);
1019 1044
1020 // update online state 1045 // update online state
1021 el.classed('online', d.online); 1046 el.classed('online', d.online);
...@@ -1026,7 +1051,12 @@ ...@@ -1026,7 +1051,12 @@
1026 attachUiBadge(svg); 1051 attachUiBadge(svg);
1027 } 1052 }
1028 1053
1029 - // TODO: update title and property values 1054 + function updAttr(id, value) {
1055 + svg.select('text.instLabel.'+id).text(value);
1056 + }
1057 +
1058 + updAttr('ip', d.ip);
1059 + updAttr('ns', nSw(d.switches));
1030 }); 1060 });
1031 1061
1032 1062
...@@ -1039,61 +1069,64 @@ ...@@ -1039,61 +1069,64 @@
1039 1069
1040 entering.each(function (d) { 1070 entering.each(function (d) {
1041 var el = d3.select(this), 1071 var el = d3.select(this),
1042 - css = window.getComputedStyle(this), 1072 + rectAttr,
1043 - w = stripPx(css.width), 1073 + svg;
1044 - h = stripPx(css.height); 1074 + instDim = computeDim(this);
1045 - 1075 + rectAttr = instRectAttr(instDim);
1046 - var svg = el.append('svg').attr({ 1076 +
1047 - width: w, 1077 + svg = el.append('svg').attr({
1048 - height: h, 1078 + width: instDim.w,
1049 - viewBox: viewBox(w, h) 1079 + height: instDim.h,
1080 + viewBox: viewBox(instDim)
1050 }); 1081 });
1051 1082
1052 - svg.append('rect') 1083 + svg.append('rect').attr(rectAttr);
1053 - .attr({
1054 - x: 8,
1055 - y: 8,
1056 - width: 104,
1057 - height: 84,
1058 - rx: 12
1059 - });
1060 1084
1061 - 1085 + appendGlyph(svg, c.nodeOx, c.nodeOy, c.nodeDim, '#node');
1062 - appendGlyph(svg, 9, 9, 36, '#node'); 1086 + appendBadge(svg, c.birdOx, c.birdOy, c.birdDim, '#bird');
1063 - appendBadge(svg, 17, 19, 21, '#bird');
1064 1087
1065 if (d.uiAttached) { 1088 if (d.uiAttached) {
1066 attachUiBadge(svg); 1089 attachUiBadge(svg);
1067 } 1090 }
1068 1091
1069 - //svg.append('use') 1092 + var left = c.nodeOx + c.nodeDim,
1070 - // .attr({ 1093 + len = rectAttr.width - left,
1071 - // class: 'birdBadge', 1094 + hlen = len / 2,
1072 - // transform: translate(8,10), 1095 + midline = hlen + left;
1073 - // 'xlink:href': '#bird', 1096 +
1074 - // width: 18, 1097 + // title
1075 - // height: 18, 1098 + svg.append('text')
1076 - // fill: '#fff' 1099 + .attr({
1077 - // }); 1100 + class: 'instTitle',
1078 - // 1101 + x: midline,
1079 - //$('<div>').attr('class', 'onosTitle').text(d.id).appendTo(el); 1102 + y: c.titleDy
1080 - 1103 + })
1081 - // is the UI attached to this instance? 1104 + .text(d.id);
1082 - // TODO: need uiAttached boolean in instance data 1105 +
1083 - // TODO: use SVG glyph, not png.. 1106 + // a couple of attributes
1084 - //if (d.uiAttached) { 1107 + var ty = c.titleDy + c.textYOff;
1085 - //if (i === 0) { 1108 +
1086 - // $('<img src="img/ui.png">').attr('class','ui').appendTo(el); 1109 + function addAttr(id, label) {
1087 - //} 1110 + svg.append('text').attr({
1111 + class: 'instLabel ' + id,
1112 + x: midline,
1113 + y: ty
1114 + }).text(label);
1115 + ty += c.textYSpc;
1116 + }
1117 +
1118 + addAttr('ip', d.ip);
1119 + addAttr('ns', nSw(d.switches));
1088 }); 1120 });
1089 1121
1090 // operate on existing + new onoses here 1122 // operate on existing + new onoses here
1091 1123
1092 - // the departed... 1124 + // adjust the panel size appropriately...
1093 - var exiting = onoses.exit() 1125 + oiBox.width(instDim.w * onosOrder.length);
1094 - .transition() 1126 + oiBox.height(instDim.h);
1095 - .style('opacity', 0) 1127 +
1096 - .remove(); 1128 + // remove any outgoing instances
1129 + onoses.exit().remove();
1097 } 1130 }
1098 1131
1099 function clickInst(d) { 1132 function clickInst(d) {
...@@ -1128,6 +1161,36 @@ ...@@ -1128,6 +1161,36 @@
1128 oiShowMaster = false; 1161 oiShowMaster = false;
1129 } 1162 }
1130 1163
1164 + // TODO: these should be moved out to utility module.
1165 + function stripPx(s) {
1166 + return s.replace(/px$/,'');
1167 + }
1168 +
1169 + function appendUse(svg, ox, oy, dim, iid, cls) {
1170 + var use = svg.append('use').attr({
1171 + transform: translate(ox,oy),
1172 + 'xlink:href': iid,
1173 + width: dim,
1174 + height: dim
1175 + });
1176 + if (cls) {
1177 + use.classed(cls, true);
1178 + }
1179 + return use;
1180 + }
1181 +
1182 + function appendGlyph(svg, ox, oy, dim, iid, cls) {
1183 + appendUse(svg, ox, oy, dim, iid, cls).classed('glyphIcon', true);
1184 + }
1185 +
1186 + function appendBadge(svg, ox, oy, dim, iid, cls) {
1187 + appendUse(svg, ox, oy, dim, iid,cls ).classed('badgeIcon', true);
1188 + }
1189 +
1190 + function attachUiBadge(svg) {
1191 + appendBadge(svg, 12, instCfg.uiDy, 30, '#uiAttached', 'uiBadge');
1192 + }
1193 +
1131 // ============================== 1194 // ==============================
1132 // force layout modification functions 1195 // force layout modification functions
1133 1196
...@@ -1763,9 +1826,12 @@ ...@@ -1763,9 +1826,12 @@
1763 1826
1764 } 1827 }
1765 1828
1766 - function find(key, array) { 1829 + function find(key, array, tag) {
1767 - for (var idx = 0, n = array.length; idx < n; idx++) { 1830 + var _tag = tag || 'key',
1768 - if (array[idx].key === key) { 1831 + idx, n, d;
1832 + for (idx = 0, n = array.length; idx < n; idx++) {
1833 + d = array[idx];
1834 + if (d[_tag] === key) {
1769 return idx; 1835 return idx;
1770 } 1836 }
1771 } 1837 }
......