GUI -- Completed ONOS Instance panel, and handling of updateInstance and removeInstance.
- minor refactorings and other cleanup. Change-Id: I4d0e467f71269f7fb91175e78d2c6af750bf9aad
Showing
8 changed files
with
219 additions
and
146 deletions
| ... | @@ -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,7 +810,9 @@ | ... | @@ -793,7 +810,9 @@ |
| 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); |
| 796 | - network.view.alert('Not yet implemented: "' + data.event + '"'); | 813 | + if (!config.useLiveData) { |
| 814 | + network.view.alert('Not yet implemented: "' + data.event + '"'); | ||
| 815 | + } | ||
| 797 | } | 816 | } |
| 798 | 817 | ||
| 799 | function unknownEvent(data) { | 818 | function unknownEvent(data) { |
| ... | @@ -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 | - | ||
| 1061 | 1084 | ||
| 1062 | - appendGlyph(svg, 9, 9, 36, '#node'); | 1085 | + appendGlyph(svg, c.nodeOx, c.nodeOy, c.nodeDim, '#node'); |
| 1063 | - appendBadge(svg, 17, 19, 21, '#bird'); | 1086 | + appendBadge(svg, c.birdOx, c.birdOy, c.birdDim, '#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 | } | ... | ... |
-
Please register or login to post a comment