Repository: qpid-dispatch Updated Branches: refs/heads/master ab4421db6 -> e149d5933
DISPATCH-1015 Clean up popups on topology page. Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/e149d593 Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/e149d593 Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/e149d593 Branch: refs/heads/master Commit: e149d5933eff11c87b467927bed3f0e56e653a81 Parents: ab4421d Author: Ernest Allen <[email protected]> Authored: Wed May 30 19:20:30 2018 -0400 Committer: Ernest Allen <[email protected]> Committed: Wed May 30 19:20:30 2018 -0400 ---------------------------------------------------------------------- console/stand-alone/index.html | 2 +- console/stand-alone/plugin/css/dispatch.css | 6 +- .../stand-alone/plugin/html/qdrTopology.html | 78 +- console/stand-alone/plugin/js/dispatchPlugin.js | 6 +- .../plugin/js/topology/qdrTopology.js | 1008 +++++------------- .../stand-alone/plugin/js/topology/traffic.js | 97 -- 6 files changed, 284 insertions(+), 913 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/e149d593/console/stand-alone/index.html ---------------------------------------------------------------------- diff --git a/console/stand-alone/index.html b/console/stand-alone/index.html index 765ad87..734fcd5 100644 --- a/console/stand-alone/index.html +++ b/console/stand-alone/index.html @@ -52,7 +52,7 @@ under the License. <nav class="navbar navbar-default navbar-pf navbar-fixed-top" role="navigation"> <div class="navbar-header"> - <button type='button' class='navbar-toggle left page-menu-button' data-toggle="collapse" data-target=".page-menu"> + <button type='button' class='navbar-toggle left page-menu-button' ng-click="pageMenuClicked()" data-toggle="collapse" data-target=".page-menu"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/e149d593/console/stand-alone/plugin/css/dispatch.css ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/css/dispatch.css b/console/stand-alone/plugin/css/dispatch.css index 976d9e5..6478620 100644 --- a/console/stand-alone/plugin/css/dispatch.css +++ b/console/stand-alone/plugin/css/dispatch.css @@ -1638,7 +1638,11 @@ svg { #overview-controller .btn {padding: 4px 12px !important;} #overview-controller .btn.filter-close {padding: 0 !important;} } - + + @-moz-document url-prefix() { + ul.options .btn {padding:0 3px !important;} + } + .ui-fancytree.fancytree-container { font-size: 14px; } http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/e149d593/console/stand-alone/plugin/html/qdrTopology.html ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/html/qdrTopology.html b/console/stand-alone/plugin/html/qdrTopology.html index 91c58a1..b99e2ef 100644 --- a/console/stand-alone/plugin/html/qdrTopology.html +++ b/console/stand-alone/plugin/html/qdrTopology.html @@ -19,33 +19,30 @@ under the License. <style> @media (min-width: 768px) { - .diagram { - padding-left: 300px; - } .showLeft { display: block; } } -@media (max-width: 768px) { - .diagram { - padding-left: 0 !important; - } +@media (max-width: 767px) { .showLeft { display: none; } - + div.qdrTopology div.legend-container.page-menu { + top: 0; + right: auto; + left: 0; + padding-right: 0; + } } -button.page-menu-button { - display: none; -} - #popover-div { +#popover-div { position: absolute; z-index: 200; border-radius: 4px; - background-color: black; - color: white; - opacity: .95; + border: 1px solid gray; + background-color: white; + color: black; + opacity: 1; padding: 12px; font-size: 14px; display: none; @@ -90,9 +87,6 @@ button.page-menu-button { #topologyForm .infoGrid div.listening-on { background-color: #336633; } - .page-menu { - width: 300px; - } .legend-container { position: absolute; @@ -177,26 +171,34 @@ li.legend-sublist > ul ul { margin-left: 1em; } -#popover-div h4 { - margin-top: 0; +#popover-div h5 { + margin-top: 1em; margin-bottom: 0; } +#popover-div h5:first-of-type { + margin-top:0; +} + +div.qdrTopology div.page-menu { + left: auto; + width: auto; + background-color: white; +} + +table.popupTable { + width: 100%; +} +table.popupTable tr.header { + color: black; + background-color: #EAEAEA; +} + +table.popupTable td { + padding: 0 4px; +} </style> <div class="qdrTopology" ng-controller="QDR.TopologyController"> - <div ng-controller="QDR.TopologyFormController"> - <div class="page-menu navbar-collapse collapse"> - <div id="topologyForm"> - <div> - <h4>{{form}} Info</h4> - <div id="formInfo"></div> - </div> - </div> - <button ng-if="panelVisible" ng-click="hideLeftPane()" class="hideLeft" title="Hide"><i class="icon-step-backward"></i></button> - </div> - <button ng-if="!panelVisible" ng-click="showLeftPane()" class="showLeft" title="Show"><i class="icon-step-forward"></i></button> - </div> - - <div class="legend-container hidden-xs"> + <div class="legend-container page-menu navbar-collapse collapse"> <uib-accordion id="topo_legend" close-others="false"> <div uib-accordion-group class="panel-default" is-open="legend.status.optionsOpen" heading="Show Traffic"> <ul class="options"> @@ -261,14 +263,6 @@ li.legend-sublist > ul ul { <li class="na" ng-class="{'force-display': isFixed()}" ng-click="setFixed(false)">Unfreeze</li> </ul> </div> - <div id="multiple_details"> - <h4 class="grid-title">Connections</h4> - <div class="grid" ui-grid="multiDetails" ui-grid-selection ui-grid-auto-resize ng-style="connectionsStyle()"></div> - </div> - <div id="link_details"> - <h4 class="grid-title">Links</h4> - <div class="grid" ui-grid="linkDetails" ui-grid-auto-resize ng-style="linksStyle()"></div> - </div> </div> <div id="popover-div" ng-bind-html="trustedpopoverContent"></div> </div> http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/e149d593/console/stand-alone/plugin/js/dispatchPlugin.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/js/dispatchPlugin.js b/console/stand-alone/plugin/js/dispatchPlugin.js index 711ce02..c39cb9b 100644 --- a/console/stand-alone/plugin/js/dispatchPlugin.js +++ b/console/stand-alone/plugin/js/dispatchPlugin.js @@ -246,7 +246,7 @@ var QDR = (function(QDR) { }); }]); - QDR.module.controller ('QDR.Core', function ($scope) { + QDR.module.controller ('QDR.Core', function ($scope, $rootScope) { $scope.alerts = []; $scope.breadcrumb = {}; $scope.closeAlert = function(index) { @@ -263,7 +263,9 @@ var QDR = (function(QDR) { $scope.alerts = []; $scope.$apply(); }); - + $scope.pageMenuClicked = function () { + $rootScope.$broadcast('pageMenuClicked'); + }; }); return QDR; http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/e149d593/console/stand-alone/plugin/js/topology/qdrTopology.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/js/topology/qdrTopology.js b/console/stand-alone/plugin/js/topology/qdrTopology.js index 1844937..74ad417 100644 --- a/console/stand-alone/plugin/js/topology/qdrTopology.js +++ b/console/stand-alone/plugin/js/topology/qdrTopology.js @@ -24,68 +24,12 @@ under the License. */ var QDR = (function(QDR) { - QDR.module.controller('QDR.TopologyFormController', function($scope, $timeout) { - - $scope.attributes = []; - $scope.attributesConnections = []; - - $scope.form = 'Router'; - $scope.$on('showEntityForm', function(event, args) { - let attributes = args.attributes; - // capitalize 1st letter - $scope.form = args.entity.charAt(0).toUpperCase() + args.entity.slice(1); - - let H = '<div class="infoGrid">'; - attributes.forEach( function (a, i) { - let even = (i % 2) ? 'even' : 'odd'; - if (a.attributeName === 'Listening on') - even += ' listening-on'; - H += ('<div class="'+ even +'"><span title="'+a.attributeName+'">' + a.attributeName + '</span><span title="'+a.attributeValue+'">' + a.attributeValue + '</span></div>'); - }); - H += '</div>'; - $('#formInfo').html(H); - - if (!$scope.$$phase) $scope.$apply(); - - }); - - $scope.panelVisible = true; // show/hide the panel on the left - $scope.hideLeftPane = function () { - d3.select('.page-menu') - .style('left' , '-360px') - .style('z-index', '1'); - - d3.select('.diagram') - .transition().duration(300).ease('sin-in') - .style('margin-left', '-360px') - .each('end', function () { - $timeout(function () {$scope.panelVisible = false;}); - let div = d3.select(this); - div.style('margin-left', '0'); - div.style('padding-left', 0); - }); - }; - $scope.showLeftPane = function () { - d3.select('.page-menu') - .style('left' , '0px'); - - $timeout(function () {$scope.panelVisible = true;}); - d3.select('.diagram') - .style('margin-left', '0px') - .transition().duration(300).ease('sin-out') - .style('margin-left', '300px') - .each('end', function () { - let div = d3.select(this); - div.style('margin-left', '0'); - div.style('padding-left', '300px'); - }); - }; - }); /** * @method TopologyController * * Controller that handles the QDR topology page */ + QDR.module.controller('QDR.TopologyController', ['$scope', '$rootScope', 'QDRService', '$location', '$timeout', '$uibModal', '$sce', function($scope, $rootScope, QDRService, $location, $timeout, $uibModal, $sce) { @@ -102,10 +46,6 @@ var QDR = (function(QDR) { urlPrefix = urlPrefix.split('#')[0]; QDR.log.debug('started QDR.TopologyController with urlPrefix: ' + urlPrefix); - $scope.multiData = []; - $scope.quiesceState = {}; - let dontHide = false; - $scope.crosshtml = $sce.trustAsHtml(''); $scope.legendOptions = angular.fromJson(localStorage[TOPOOPTIONSKEY]) || {showTraffic: false, trafficType: 'dots'}; if (!$scope.legendOptions.trafficType) $scope.legendOptions.trafficType = 'dots'; @@ -135,211 +75,6 @@ var QDR = (function(QDR) { } }); - $scope.quiesceConnection = function(row) { - let entity = row.entity; - let state = $scope.quiesceState[entity.connectionId].state; - if (state === 'enabled') { - // start quiescing all links - $scope.quiesceState[entity.connectionId].state = 'quiescing'; - } else if (state === 'quiesced') { - // start reviving all links - $scope.quiesceState[entity.connectionId].state = 'reviving'; - } - $scope.multiDetails.updateState(entity); - dontHide = true; - $scope.multiDetails.selectRow(row.rowIndex, true); - $scope.multiDetails.showLinksList(row); - }; - $scope.quiesceDisabled = function(row) { - return $scope.quiesceState[row.entity.connectionId].buttonDisabled; - }; - $scope.quiesceText = function(row) { - return $scope.quiesceState[row.entity.connectionId].buttonText; - }; - $scope.quiesceClass = function(row) { - const stateClassMap = { - enabled: 'btn-primary', - quiescing: 'btn-warning', - reviving: 'btn-warning', - quiesced: 'btn-danger' - }; - return stateClassMap[$scope.quiesceState[row.entity.connectionId].state]; - }; - - // This is the grid that shows each connection when a client node that represents multiple connections is clicked - $scope.multiData = []; - $scope.multiDetails = { - data: 'multiData', - enableColumnResize: true, - enableHorizontalScrollbar: 0, - enableVerticalScrollbar: 0, - jqueryUIDraggable: true, - enablePaging: false, - multiSelect: false, - enableSelectAll: false, - enableSelectionBatchEvent: false, - enableRowHeaderSelection: false, - noUnselect: true, - onRegisterApi: function (gridApi) { - if (gridApi.selection) { - gridApi.selection.on.rowSelectionChanged($scope, function(row){ - let detailsDiv = d3.select('#link_details'); - let isVis = detailsDiv.style('display') === 'block'; - if (!dontHide && isVis && $scope.connectionId === row.entity.connectionId) { - hideLinkDetails(); - return; - } - dontHide = false; - $scope.multiDetails.showLinksList(row); - }); - } - }, - showLinksList: function(obj) { - $scope.linkData = obj.entity.linkData; - $scope.connectionId = obj.entity.connectionId; - let visibleLen = Math.min(obj.entity.linkData.length, 10); - //QDR.log.debug("visibleLen is " + visibleLen) - let left = parseInt(d3.select('#multiple_details').style('left'), 10); - let offset = $('#topology').offset(); - let detailsDiv = d3.select('#link_details'); - detailsDiv - .style({ - display: 'block', - opacity: 1, - left: (left + 20) + 'px', - top: (mouseY - offset.top + 20 + $(document).scrollTop()) + 'px', - height: ((visibleLen + 1) * 30) + 40 + 'px', // +1 for the header row - 'overflow-y': obj.entity.linkData > 10 ? 'scroll' : 'hidden' - }); - }, - updateState: function(entity) { - let state = $scope.quiesceState[entity.connectionId].state; - - // count enabled and disabled links for this connection - let enabled = 0, - disabled = 0; - entity.linkData.forEach(function(link) { - if (link.adminStatus === 'enabled') - ++enabled; - if (link.adminStatus === 'disabled') - ++disabled; - }); - - let linkCount = entity.linkData.length; - // if state is quiescing and any links are enabled, button should say 'Quiescing' and be disabled - if (state === 'quiescing' && (enabled > 0)) { - $scope.quiesceState[entity.connectionId].buttonText = 'Quiescing'; - $scope.quiesceState[entity.connectionId].buttonDisabled = true; - } else - // if state is enabled and all links are disabled, button should say Revive and be enabled. set state to quisced - // if state is quiescing and all links are disabled, button should say 'Revive' and be enabled. set state to quiesced - if ((state === 'quiescing' || state === 'enabled') && (disabled === linkCount)) { - $scope.quiesceState[entity.connectionId].buttonText = 'Revive'; - $scope.quiesceState[entity.connectionId].buttonDisabled = false; - $scope.quiesceState[entity.connectionId].state = 'quiesced'; - } else - // if state is reviving and any links are disabled, button should say 'Reviving' and be disabled - if (state === 'reviving' && (disabled > 0)) { - $scope.quiesceState[entity.connectionId].buttonText = 'Reviving'; - $scope.quiesceState[entity.connectionId].buttonDisabled = true; - } else - // if state is reviving or quiesced and all links are enabled, button should say 'Quiesce' and be enabled. set state to enabled - if ((state === 'reviving' || state === 'quiesced') && (enabled === linkCount)) { - $scope.quiesceState[entity.connectionId].buttonText = 'Quiesce'; - $scope.quiesceState[entity.connectionId].buttonDisabled = false; - $scope.quiesceState[entity.connectionId].state = 'enabled'; - } - }, - columnDefs: [{ - field: 'host', - cellTemplate: 'titleCellTemplate.html', - //headerCellTemplate: 'titleHeaderCellTemplate.html', - displayName: 'Connection host' - }, { - field: 'user', - cellTemplate: 'titleCellTemplate.html', - //headerCellTemplate: 'titleHeaderCellTemplate.html', - displayName: 'User' - }, { - field: 'properties', - cellTemplate: 'titleCellTemplate.html', - //headerCellTemplate: 'titleHeaderCellTemplate.html', - displayName: 'Properties' - } - /*, - { - cellClass: 'gridCellButton', - cellTemplate: '<button title="{{quiesceText(row)}} the links" type="button" ng-class="quiesceClass(row)" class="btn" ng-click="$event.stopPropagation();quiesceConnection(row)" ng-disabled="quiesceDisabled(row)">{{quiesceText(row)}}</button>' - }*/ - ] - }; - $scope.quiesceLinkClass = function(row) { - const stateClassMap = { - enabled: 'btn-primary', - disabled: 'btn-danger' - }; - return stateClassMap[row.entity.adminStatus]; - }; - $scope.quiesceLink = function(row) { - QDRService.management.topology.quiesceLink(row.entity.nodeId, row.entity.name) - .then( function (results, context) { - let statusCode = context.message.application_properties.statusCode; - if (statusCode < 200 || statusCode >= 300) { - QDR.Core.notification('error', context.message.statusDescription); - QDR.log.info('Error ' + context.message.statusDescription); - } - }); - }; - $scope.quiesceLinkDisabled = function(row) { - return (row.entity.operStatus !== 'up' && row.entity.operStatus !== 'down'); - }; - $scope.quiesceLinkText = function(row) { - return row.entity.operStatus === 'down' ? 'Revive' : 'Quiesce'; - }; - $scope.linkData = []; - $scope.linkDetails = { - data: 'linkData', - jqueryUIDraggable: true, - columnDefs: [{ - field: 'adminStatus', - cellTemplate: 'titleCellTemplate.html', - headerCellTemplate: 'titleHeaderCellTemplate.html', - displayName: 'Admin state' - }, { - field: 'operStatus', - cellTemplate: 'titleCellTemplate.html', - headerCellTemplate: 'titleHeaderCellTemplate.html', - displayName: 'Oper state' - }, { - field: 'dir', - cellTemplate: 'titleCellTemplate.html', - headerCellTemplate: 'titleHeaderCellTemplate.html', - displayName: 'dir' - }, { - field: 'owningAddr', - cellTemplate: 'titleCellTemplate.html', - headerCellTemplate: 'titleHeaderCellTemplate.html', - displayName: 'Address' - }, { - field: 'deliveryCount', - displayName: 'Delivered', - headerCellTemplate: 'titleHeaderCellTemplate.html', - cellClass: 'grid-values' - - }, { - field: 'uncounts', - displayName: 'Outstanding', - headerCellTemplate: 'titleHeaderCellTemplate.html', - cellClass: 'grid-values' - } - /*, - { - cellClass: 'gridCellButton', - cellTemplate: '<button title="{{quiesceLinkText(row)}} this link" type="button" ng-class="quiesceLinkClass(row)" class="btn" ng-click="quiesceLink(row)" ng-disabled="quiesceLinkDisabled(row)">{{quiesceLinkText(row)}}</button>' - }*/ - ] - }; - // mouse event vars let selected_node = null, selected_link = null, @@ -412,15 +147,14 @@ var QDR = (function(QDR) { let height = 0; var getSizes = function() { - let legendWidth = 143; - let display = $('#topo_svg_legend').css('display'); - if (display === 'none') - legendWidth = 0; const gap = 5; + let legendWidth = 194; + let topoWidth = $('#topology').width(); + if (topoWidth < 768) + legendWidth = 0; let width = $('#topology').width() - gap - legendWidth; let top = $('#topology').offset().top; - let tpformHeight = $('#topologyForm').height(); - let height = Math.max(window.innerHeight, tpformHeight + top) - top - gap; + let height = window.innerHeight - top - gap; if (width < 10) { QDR.log.info('page width and height are abnormal w:' + width + ' height:' + height); return [0, 0]; @@ -439,11 +173,15 @@ var QDR = (function(QDR) { svg.attr('height', height); force.size(sizes).resume(); } + $timeout(createLegend); }; - $scope.$on('panel-resized', function () { - resize(); + // the window is narrow and the page menu icon was clicked. + // Re-create the legend + $scope.$on('pageMenuClicked', function () { + $timeout(createLegend); }); + window.addEventListener('resize', resize); let sizes = getSizes(); width = sizes[0]; @@ -707,16 +445,7 @@ var QDR = (function(QDR) { .append('svg') .attr('id', 'SVG_ID') .attr('width', width) - .attr('height', height) - .on('click', function() { - removeCrosssection(); - }); - - $(document).keyup(function(e) { - if (e.keyCode === 27) { - removeCrosssection(); - } - }); + .attr('height', height); // the legend d3.select('#topo_svg_legend svg').remove(); @@ -811,9 +540,6 @@ var QDR = (function(QDR) { } }); } - setTimeout(function () { - updateForm(Object.keys(QDRService.management.topology.nodeInfo())[0], 'router', 0); - }); // if any clients don't yet have link directions, get the links for those nodes and restart the graph if (unknowns.length > 0) @@ -837,7 +563,8 @@ var QDR = (function(QDR) { } unknownNodes = Object.keys(unknownNodes); //QDR.log.info("-- resolveUnknowns: ensuring .connection and .router.link are present for each node") - QDRService.management.topology.ensureEntities(unknownNodes, [{entity: 'connection', force: true}, {entity: 'router.link', attrs: ['linkType','connectionId','linkDir'], force: true}], function () { + QDRService.management.topology.ensureEntities(unknownNodes, [{entity: 'connection', force: true}, + {entity: 'router.link', attrs: ['linkType','connectionId','linkDir'], force: true}], function () { nodeInfo = QDRService.management.topology.nodeInfo(); initializeLinks(nodeInfo, []); // collapse any router-container nodes that are duplicates @@ -847,68 +574,6 @@ var QDR = (function(QDR) { }); }; - function updateForm(key, entity, resultIndex) { - if (!angular.isDefined(resultIndex)) - return; - let nodeList = QDRService.management.topology.nodeIdList(); - if (nodeList.indexOf(key) > -1) { - QDRService.management.topology.fetchEntities(key, [ - {entity: entity}, - {entity: 'listener', attrs: ['role', 'port']}], function (results) { - let onode = results[key]; - if (!onode[entity]) { - console.log('requested ' + entity + ' but didn\'t get it'); - return; - } - let nodeResults = onode[entity].results[resultIndex]; - let nodeAttributes = onode[entity].attributeNames; - let attributes = nodeResults.map(function(row, i) { - return { - attributeName: nodeAttributes[i], - attributeValue: row - }; - }); - // sort by attributeName - attributes.sort(function(a, b) { - return a.attributeName.localeCompare(b.attributeName); - }); - - // move the Name first - let nameIndex = attributes.findIndex(function(attr) { - return attr.attributeName === 'name'; - }); - if (nameIndex >= 0) - attributes.splice(0, 0, attributes.splice(nameIndex, 1)[0]); - - // get the list of ports this router is listening on - if (entity === 'router') { - let listeners = onode['listener'].results; - let listenerAttributes = onode['listener'].attributeNames; - let normals = listeners.filter(function(listener) { - return QDRService.utilities.valFor(listenerAttributes, listener, 'role') === 'normal'; - }); - let ports = []; - normals.forEach(function(normalListener) { - ports.push(QDRService.utilities.valFor(listenerAttributes, normalListener, 'port')); - }); - // add as 2nd row - if (ports.length) { - attributes.splice(1, 0, { - attributeName: 'Listening on', - attributeValue: ports, - description: 'The port(s) on which this router is listening for connections' - }); - } - } - $rootScope.$broadcast('showEntityForm', { - entity: entity, - attributes: attributes - }); - if (!$scope.$$phase) $scope.$apply(); - }); - } - } - function getContainerIndex(_id, nodeInfo) { let nodeIndex = 0; for (let id in nodeInfo) { @@ -1063,10 +728,6 @@ var QDR = (function(QDR) { return links[i]; } // the selected node was a client/broker - //QDR.log.debug("failed to find a link between "); - //console.dump(source); - //QDR.log.debug(" and "); - //console.dump(target); return null; } @@ -1079,34 +740,6 @@ var QDR = (function(QDR) { } - function removeCrosssection() { - d3.select('#crosssection svg g').transition() - .duration(1000) - .attr('transform', 'scale(0)') - .style('opacity', 0) - .each('end', function () { - d3.select('#crosssection svg').remove(); - d3.select('#crosssection').style('display','none'); - }); - d3.select('#multiple_details').transition() - .duration(500) - .style('opacity', 0) - .each('end', function() { - d3.select('#multiple_details').style('display', 'none'); - stopUpdateConnectionsGrid(); - }); - hideLinkDetails(); - } - - function hideLinkDetails() { - d3.select('#link_details').transition() - .duration(500) - .style('opacity', 0) - .each('end', function() { - d3.select('#link_details').style('display', 'none'); - }); - } - function clerAllHighlights() { for (let i = 0; i < links.length; ++i) { links[i]['highlighted'] = false; @@ -1162,55 +795,25 @@ var QDR = (function(QDR) { return d.cls == 'small'; }) .on('mouseover', function(d) { // mouse over a path - let resultIndex = 0; // the connection to use - let left = d.left ? d.target : d.source; - // right is the node that the arrow points to, left is the other node - let right = d.left ? d.source : d.target; - let onode = QDRService.management.topology.nodeInfo()[left.key]; - // loop through all the connections for left, and find the one for right - if (!onode || !onode['connection']) - return; - // update the info dialog for the link the mouse is over - if (!selected_node && !selected_link) { - for (resultIndex = 0; resultIndex < onode['connection'].results.length; ++resultIndex) { - let conn = onode['connection'].results[resultIndex]; - /// find the connection whose container is the right's name - let name = QDRService.utilities.valFor(onode['connection'].attributeNames, conn, 'container'); - if (name == right.routerId) { - break; - } - } - // did not find connection. this is a connection to a non-interrouter node - if (resultIndex === onode['connection'].results.length) { - // use the non-interrouter node's connection info - left = d.target; - resultIndex = left.resultIndex; - } - updateForm(left.key, 'connection', resultIndex); - } - + let event = d3.event; mousedown_link = d; selected_link = mousedown_link; - restart(); - }) - .on('mousemove', function (d) { let updateTooltip = function () { $timeout(function () { $scope.trustedpopoverContent = $sce.trustAsHtml(connectionPopupHTML(d)); + if (selected_link) + displayTooltip(event); }); }; // update the contents of the popup tooltip each time the data is polled QDRService.management.topology.addUpdatedAction('connectionPopupHTML', updateTooltip); - + QDRService.management.topology.ensureAllEntities( + [{ entity: 'router.link', force: true},{entity: 'connection'}], function () { + updateTooltip(); + }); // show the tooltip - let top = $('#topology').offset().top - 5; - d3.select('#popover-div') - .style('display', 'block') - .style('left', (d3.event.pageX+5)+'px') - .style('top', (d3.event.pageY-top)+'px'); - - // update the tooltip right now updateTooltip(); + restart(); }) .on('mouseout', function() { // mouse out of a path @@ -1221,166 +824,9 @@ var QDR = (function(QDR) { restart(); }) // left click a path - .on('click', function (d) { - let clickPos = d3.mouse(this); + .on('click', function () { d3.event.stopPropagation(); clearPopups(); - var showCrossSection = function() { - const diameter = 400; - let pack = d3.layout.pack() - .size([diameter - 4, diameter - 4]) - .padding(-10) - .value(function(d) { return d.size; }); - - d3.select('#crosssection svg').remove(); - let svg = d3.select('#crosssection').append('svg') - .attr('width', diameter) - .attr('height', diameter); - - let rg = svg.append('svg:defs') - .append('radialGradient') - .attr('id', 'cross-gradient') - .attr('gradientTransform', 'scale(2.0) translate(-0.5,-0.5)'); - - rg - .append('stop') - .attr('offset', '0%') - .attr('stop-color', '#feffff'); - rg - .append('stop') - .attr('offset', '40%') - .attr('stop-color', '#cfe2f3'); - - let svgg = svg.append('g') - .attr('transform', 'translate(2,2)'); - - svgg - .append('rect') - .attr('x', 0) - .attr('y', 0) - .attr('width', 200) - .attr('height', 200) - .attr('class', 'cross-rect') - .attr('fill', 'url('+urlPrefix+'#cross-gradient)'); - - svgg - .append('line') - .attr('class', 'cross-line') - .attr({x1: 2, y1: 0, x2: 200, y2: 0}); - svgg - .append('line') - .attr('class', 'cross-line') - .attr({x1: 2, y1: 0, x2: 0, y2: 200}); - - /* - let simpleLine = d3.svg.line(); - svgg - .append('path') - .attr({ - d: simpleLine([[0,0],[0,200]]), - stroke: '#000', - 'stroke-width': '4px' - }); - svgg - .append('path') - .attr({ - d: simpleLine([[0,0],[200,0]]), - stroke: '#000', - 'stroke-width': '4px' - }); -*/ - let root = { - name: ' Links between ' + d.source.name + ' and ' + d.target.name, - children: [] - }; - let nodeInfo = QDRService.management.topology.nodeInfo(); - let connections = nodeInfo[d.source.key]['connection']; - let containerIndex = connections.attributeNames.indexOf('container'); - connections.results.some ( function (connection) { - if (connection[containerIndex] == d.target.routerId) { - root.attributeNames = connections.attributeNames; - root.obj = connection; - root.desc = 'Connection'; - return true; // stop looping after 1 match - } - return false; - }); - - // find router.links where link.remoteContainer is d.source.name - let links = nodeInfo[d.source.key]['router.link']; - let identityIndex = connections.attributeNames.indexOf('identity'); - let roleIndex = connections.attributeNames.indexOf('role'); - let connectionIdIndex = links.attributeNames.indexOf('connectionId'); - let linkTypeIndex = links.attributeNames.indexOf('linkType'); - let nameIndex = links.attributeNames.indexOf('name'); - let linkDirIndex = links.attributeNames.indexOf('linkDir'); - - if (roleIndex < 0 || identityIndex < 0 || connectionIdIndex < 0 - || linkTypeIndex < 0 || nameIndex < 0 || linkDirIndex < 0) - return; - links.results.forEach ( function (link) { - if (root.obj && link[connectionIdIndex] == root.obj[identityIndex] && link[linkTypeIndex] == root.obj[roleIndex]) - root.children.push ( - { name: ' ' + link[linkDirIndex] + ' ', - size: 100, - obj: link, - desc: 'Link', - attributeNames: links.attributeNames - }); - }); - if (root.children.length == 0) - return; - let node = svgg.datum(root).selectAll('.node') - .data(pack.nodes) - .enter().append('g') - .attr('class', function(d) { return d.children ? 'parent node hastip' : 'leaf node hastip'; }) - .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')' + (!d.children ? 'scale(0.9)' : ''); }); - node.append('circle') - .attr('r', function(d) { return d.r; }); - - node.on('mouseenter', function (d) { - let title = '<h4>' + d.desc + '</h4><table class=\'tiptable\'><tbody>'; - if (d.attributeNames) - d.attributeNames.forEach( function (n, i) { - title += '<tr><td>' + n + '</td><td>'; - title += d.obj[i] != null ? d.obj[i] : ''; - title += '</td></tr>'; - }); - title += '</tbody></table>'; - $timeout( (function () { - $scope.crosshtml = $sce.trustAsHtml(title); - $('#crosshtml').show(); - let parent = $('#crosssection'); - let ppos = parent.position(); - let mleft = ppos.left + parent.width(); - $('#crosshtml').css({left: mleft, top: ppos.top}); - }).bind(this)); - }); - node.on('mouseout', function () { - $('#crosshtml').hide(); - }); - - node.append('text') - .attr('dy', function (d) { return d.children ? '-10em' : '.5em';}) - .style('text-anchor', 'middle') - .text(function(d) { - return d.name.substring(0, d.r / 3); - }); - svgg.attr('transform', 'translate(2,2) scale(0.01)'); - - let bounds = $('#topology').position(); - d3.select('#crosssection') - .style('display', 'block') - .style('left', (clickPos[0] + bounds.left) + 'px') - .style('top', (clickPos[1] + bounds.top) + 'px'); - - svgg.transition() - .attr('transform', 'translate(2,2) scale(1)') - .each('end', function () { - d3.selectAll('#crosssection g.leaf text').attr('dy', '.3em'); - }); - }; - QDRService.management.topology.ensureEntities(d.source.key, {entity: 'router.link', force: true}, showCrossSection); }); // remove old links path.exit().remove(); @@ -1404,7 +850,7 @@ var QDR = (function(QDR) { return d.fixed & 1; }); - // add new circle nodes. if nodes[] is longer than the existing paths, add a new path for each new element + // add new circle nodes let g = circle.enter().append('svg:g') .classed('multiple', function(d) { return (d.normals && d.normals.length > 1); @@ -1413,28 +859,19 @@ var QDR = (function(QDR) { appendCircle(g) .on('mouseover', function(d) { // mouseover a circle - if (!selected_node && !mousedown_node) { - if (d.nodeType === 'inter-router') { - //QDR.log.debug("showing general form"); - updateForm(d.key, 'router', 0); - } else if (d.nodeType === 'normal' || d.nodeType === 'on-demand' || d.nodeType === 'route-container') { - //QDR.log.debug("showing connections form"); - updateForm(d.key, 'connection', d.resultIndex); - } - } - + QDRService.management.topology.delUpdatedAction('connectionPopupHTML'); + if (d.nodeType === 'normal') { + showClientTooltip(d, d3.event); + } else + showRouterTooltip(d, d3.event); if (d === mousedown_node) return; - //if (d === selected_node) - // return; // enlarge target node d3.select(this).attr('transform', 'scale(1.1)'); - // highlight the next-hop route from the selected node to this node - //mousedown_node = null; - if (!selected_node) { return; } + // highlight the next-hop route from the selected node to this node clerAllHighlights(); // we need .router.node info to highlight hops QDRService.management.topology.ensureAllEntities([{entity: 'router.node', attrs: ['id','nextHop']}], function () { @@ -1445,6 +882,8 @@ var QDR = (function(QDR) { }) .on('mouseout', function() { // mouse out for a circle // unenlarge target node + d3.select('#popover-div') + .style('display', 'none'); d3.select(this).attr('transform', ''); clerAllHighlights(); mouseover_node = null; @@ -1533,26 +972,24 @@ var QDR = (function(QDR) { return; } d3.event.stopPropagation(); - startUpdateConnectionsGrid(d); }); appendContent(g); - appendTitle(g); + //appendTitle(g); // remove old nodes circle.exit().remove(); - // add subcircles - svg.selectAll('.subcircle').remove(); + // add text to client circles if there are any that represent multiple clients + svg.selectAll('.subtext').remove(); let multiples = svg.selectAll('.multiple'); multiples.each(function(d) { - d.normals.forEach(function(n, i) { - if (i < d.normals.length - 1 && i < 3) // only show a few shadow circles - this.insert('svg:circle', ':first-child') - .attr('class', 'subcircle node') - .attr('r', 15 - i) - .attr('transform', 'translate(' + 4 * (i + 1) + ', 0)'); - }, d3.select(this)); + let g = d3.select(this); + g.append('svg:text') + .attr('x', radiusNormal + 3) + .attr('y', Math.floor(radiusNormal / 2)) + .attr('class', 'subtext') + .text('x ' + d.normals.length); }); // call createLegend in timeout because: // If we create the legend right away, then it will be destroyed when the accordian @@ -1751,164 +1188,99 @@ var QDR = (function(QDR) { }; let appendTitle = function(g) { g.append('svg:title').text(function(d) { - let x = ''; - if (d.normals && d.normals.length > 1) - x = ' x ' + d.normals.length; - if (QDRService.utilities.isConsole(d)) { - return 'Dispatch console' + x; - } else if (QDRService.utilities.isArtemis(d)) { - return 'Broker - Artemis' + x; - } else if (d.properties.product == 'qpid-cpp') { - return 'Broker - qpid-cpp' + x; - } else if (d.properties.product) { - return d.properties.product; - } else if (d.cdir === 'in') - return 'Sender' + x; - else if (d.cdir === 'out') - return 'Receiver' + x; - else if (d.cdir === 'both') - return 'Sender/Receiver' + x; - return d.nodeType == 'normal' ? 'client' + x : (d.nodeType == 'on-demand' ? 'broker' : 'Router ' + d.name); + return generateTitle(d); }); }; - var startUpdateConnectionsGrid = function(d) { - // called after each topology update - var extendConnections = function() { - // force a fetch of the links for this node - QDRService.management.topology.ensureEntities(d.key, {entity: 'router.link', force: true}, function () { - // the links for this node are now available - $scope.multiData = []; - let normals = d.normals; - // find updated normals for d - d3.selectAll('.normal') - .each(function(newd) { - if (newd.id == d.id && newd.name == d.name) { - normals = newd.normals; - } - }); - if (normals) { - normals.forEach(function(n) { - let nodeInfo = QDRService.management.topology.nodeInfo(); - let links = nodeInfo[n.key]['router.link']; - let linkTypeIndex = links.attributeNames.indexOf('linkType'); - let connectionIdIndex = links.attributeNames.indexOf('connectionId'); - n.linkData = []; - links.results.forEach(function(link) { - if (link[linkTypeIndex] === 'endpoint' && link[connectionIdIndex] === n.connectionId) { - let l = {}; - let ll = QDRService.utilities.flatten(links.attributeNames, link); - l.owningAddr = ll.owningAddr; - l.dir = ll.linkDir; - if (l.owningAddr && l.owningAddr.length > 2) - if (l.owningAddr[0] === 'M') - l.owningAddr = l.owningAddr.substr(2); - else - l.owningAddr = l.owningAddr.substr(1); - - l.deliveryCount = ll.deliveryCount; - l.uncounts = QDRService.utilities.pretty(ll.undeliveredCount + ll.unsettledCount); - l.adminStatus = ll.adminStatus; - l.operStatus = ll.operStatus; - l.identity = ll.identity; - l.connectionId = ll.connectionId; - l.nodeId = n.key; - l.type = ll.type; - l.name = ll.name; - - // TODO: remove this fake quiescing/reviving logic when the routers do the work - initConnState(n.connectionId); - if ($scope.quiesceState[n.connectionId].linkStates[l.identity]) - l.adminStatus = $scope.quiesceState[n.connectionId].linkStates[l.identity]; - if ($scope.quiesceState[n.connectionId].state == 'quiescing') { - if (l.adminStatus === 'enabled') { - // 25% chance of switching - let chance = Math.floor(Math.random() * 2); - if (chance == 1) { - l.adminStatus = 'disabled'; - $scope.quiesceState[n.connectionId].linkStates[l.identity] = 'disabled'; - } - } - } - if ($scope.quiesceState[n.connectionId].state == 'reviving') { - if (l.adminStatus === 'disabled') { - // 25% chance of switching - let chance = Math.floor(Math.random() * 2); - if (chance == 1) { - l.adminStatus = 'enabled'; - $scope.quiesceState[n.connectionId].linkStates[l.identity] = 'enabled'; - } - } - } - QDR.log.debug('pushing link state for ' + l.owningAddr + ' status: ' + l.adminStatus); - - n.linkData.push(l); - } - }); - $scope.multiData.push(n); - if (n.connectionId == $scope.connectionId) - $scope.linkData = n.linkData; - initConnState(n.connectionId); - $scope.multiDetails.updateState(n); - }); - } - $scope.$apply(); - - d3.select('#multiple_details') - .style({ - height: ((normals.length + 1) * 30) + 40 + 'px', - 'overflow-y': normals.length > 10 ? 'scroll' : 'hidden' - }); - }); - }; - $scope.connectionsStyle = function () { - return { - height: ($scope.multiData.length * 30 + 40) + 'px' - }; - }; - $scope.linksStyle = function () { - return { - height: ($scope.linkData.length * 30 + 40) + 'px' - }; - }; - // register a notification function for when the topology is updated - QDRService.management.topology.addUpdatedAction('normalsStats', extendConnections); - // call the function that gets the links right now - extendConnections(); - clearPopups(); - let display = 'block'; - if (d.normals.length === 1) { - display = 'none'; - mouseY = mouseY - 20; + let generateTitle = function (d) { + let x = ''; + if (d.normals && d.normals.length > 1) + x = ' x ' + d.normals.length; + if (QDRService.utilities.isConsole(d)) + return 'Dispatch console' + x; + else if (QDRService.utilities.isArtemis(d)) + return 'Broker - Artemis' + x; + else if (d.properties.product == 'qpid-cpp') + return 'Broker - qpid-cpp' + x; + else if (d.cdir === 'in') + return 'Sender' + x; + else if (d.cdir === 'out') + return 'Receiver' + x; + else if (d.cdir === 'both') + return 'Sender/Receiver' + x; + else if (d.nodeType === 'normal') + return 'client' + x; + else if (d.nodeType === 'on-demand') + return 'broker'; + else if (d.properties.product) { + return d.properties.product; } - let rm = relativeMouse(); - d3.select('#multiple_details') - .style({ - display: display, - opacity: 1, - left: rm.left + 'px', - top: (rm.top - rm.offset.top) + 'px' - }); - if (d.normals.length === 1) { - // simulate a click on the connection to popup the link details - QDRService.management.topology.ensureEntities(d.key, {entity: 'router.link', force: true}, function () { - $scope.multiDetails.showLinksList({entity: d}); - }); + else { + return ''; } }; - var stopUpdateConnectionsGrid = function() { - QDRService.management.topology.delUpdatedAction('normalsStats'); + + let showClientTooltip = function (d, event) { + let type = generateTitle(d); + let title = '<table class="popupTable"><tr><td>Type</td><td>' + type + '</td></tr>'; + if (!d.normals || d.normals.length < 2) + title += ('<tr><td>Host</td><td>' + d.host + '</td></tr>'); + title += '</table>'; + showToolTip(title, event); }; - var initConnState = function(id) { - if (!angular.isDefined($scope.quiesceState[id])) { - $scope.quiesceState[id] = { - state: 'enabled', - buttonText: 'Quiesce', - buttonDisabled: false, - linkStates: {} - }; - } + let showRouterTooltip = function (d, event) { + QDRService.management.topology.ensureEntities(d.key, [ + {entity: 'listener', attrs: ['role', 'port', 'http']}, + {entity: 'router', attrs: ['name', 'version', 'hostName']} + ], function () { + // update all the router title text + let nodes = QDRService.management.topology.nodeInfo(); + let node = nodes[d.key]; + let listeners = node['listener']; + let router = node['router']; + let r = QDRService.utilities.flatten(router.attributeNames, router.results[0]); + let title = '<table class="popupTable">'; + title += ('<tr><td>Router</td><td>' + r.name + '</td></tr>'); + if (r.hostName) + title += ('<tr><td>Host Name</td><td>' + r.hostHame + '</td></tr>'); + title += ('<tr><td>Version</td><td>' + r.version + '</td></tr>'); + let ports = []; + for (let l=0; l<listeners.results.length; l++) { + let listener = QDRService.utilities.flatten(listeners.attributeNames, listeners.results[l]); + if (listener.role === 'normal') { + ports.push(listener.port+''); + } + } + if (ports.length > 0) { + title += ('<tr><td>Ports</td><td>' + ports.join(', ') + '</td></tr>'); + } + title += '</table>'; + showToolTip(title, event); + }); + }; + let showToolTip = function (title, event) { + // show the tooltip + $timeout ( function () { + $scope.trustedpopoverContent = $sce.trustAsHtml(title); + displayTooltip(event); + }); + }; + + let displayTooltip = function (event) { + $timeout( function () { + let top = $('#topology').offset().top - 5; + let width = $('#topology').width(); + d3.select('#popover-div') + .style('visibility', 'hidden') + .style('display', 'block') + .style('left', (event.pageX+5)+'px') + .style('top', (event.pageY-top)+'px'); + let pwidth = $('#popover-div').width(); + d3.select('#popover-div') + .style('visibility', 'visible') + .style('left',(Math.min(width-pwidth, event.pageX+5) + 'px')); + }); }; function nextHop(thisNode, d, cb) { @@ -2040,7 +1412,6 @@ var QDR = (function(QDR) { force.nodes(nodes).links(links).start(); restart(); } - //initForceGraph(); } else { //QDR.log.debug("topology didn't change") @@ -2048,13 +1419,10 @@ var QDR = (function(QDR) { }); } - function setupInitialUpdate() { // make sure all router nodes have .connection info. if not then fetch any missing info QDRService.management.topology.ensureAllEntities( - // [{entity: ".connection"}, {entity: ".router.lin.router.link", attrs: ["linkType","connectionId","linkDir"]}], [{entity: 'connection'}], - //[{entity: ".connection"}], handleInitialUpdate); } if (!QDRService.management.connection.is_connected()) { @@ -2064,6 +1432,102 @@ var QDR = (function(QDR) { } let connectionPopupHTML = function (d) { + let getConnsArray = function (d, conn) { + let conns = [conn]; + if (d.cls === 'small') { + conns = []; + let normals = d.target.normals ? d.target.normals : d.source.normals; + for (let n=0; n<normals.length; n++) { + if (normals[n].resultIndex !== undefined) { + conns.push(QDRService.utilities.flatten(onode['connection'].attributeNames, + onode['connection'].results[normals[n].resultIndex])); + } + } + } + return conns; + }; + // construct HTML to be used in a popup when the mouse is moved over a link. + // The HTML is sanitized elsewhere before it is displayed + let linksHTML = function (onode, conn, d) { + const max_links = 10; + const fields = ['undelivered', 'unsettled', 'rejected', 'released', 'modified']; + // local function to determine if a link's connectionId is in any of the connections + let isLinkFor = function (connectionId, conns) { + for (let c=0; c<conns.length; c++) { + if (conns[c].identity === connectionId) + return true; + } + return false; + }; + let fnJoin = function (ar, sepfn) { + let out = ''; + out = ar[0]; + for (let i=1; i<ar.length; i++) { + let sep = sepfn(ar[i]); + out += (sep[0] + sep[1]); + } + return out; + }; + let conns = getConnsArray(d, conn); + // if the data for the line is from a client (small circle), we may have multiple connections + // loop through all links for this router and accumulate those belonging to the connection(s) + let nodeLinks = onode['router.link']; + if (!nodeLinks) + return ''; + let links = []; + let hasAddress = false; + for (let n=0; n<nodeLinks.results.length; n++) { + let link = QDRService.utilities.flatten(nodeLinks.attributeNames, nodeLinks.results[n]); + if (link.linkType !== 'router-control') { + if (isLinkFor(link.connectionId, conns)) { + if (link.owningAddr) + hasAddress = true; + links.push(link); + } + } + } + // we may need to limit the number of links displayed, so sort descending by the sum of the field values + links.sort( function (a, b) { + let asum = a.undeliveredCount + a.unsettledCount + a.rejectedCount + a.releasedCount + a.modifiedCount; + let bsum = b.undeliveredCount + b.unsettledCount + b.rejectedCount + b.releasedCount + b.modifiedCount; + return asum < bsum ? 1 : asum > bsum ? -1 : 0; + }); + let HTMLHeading = '<h5>Links</h5>'; + let HTML = '<table class="popupTable">'; + // copy of fields since we may be prepending an address + let th = fields.slice(); + // convert to actual attribute names + let td = fields.map( function (f) {return f + 'Count';}); + th.unshift('dir'); + td.unshift('linkDir'); + // add an address field if any of the links had an owningAddress + if (hasAddress) { + th.unshift('address'); + td.unshift('owningAddr'); + } + HTML += ('<tr class="header"><td>' + th.join('</td><td>') + '</td></tr>'); + // add rows to the table for each link + for (let l=0; l<links.length; l++) { + if (l>=max_links) { + HTMLHeading = '<h4>Top ' + max_links + ' Links</h4>'; + break; + } + let link = links[l]; + let vals = td.map( function (f) { + if (f === 'owningAddr') { + let identity = QDRService.utilities.identity_clean(link.owningAddr); + return QDRService.utilities.addr_text(identity); + } + return link[f]; + }); + let joinedVals = fnJoin(vals, function (v1) { + return ['</td><td' + (isNaN(+v1) ? '': ' align="right"') + '>', QDRService.utilities.pretty(v1 || '0')]; + }); + HTML += ('<tr><td>' + joinedVals + '</td></tr>'); + } + HTML += '</table>'; + return HTMLHeading + HTML; + }; let left = d.left ? d.source : d.target; // left is the connection with dir 'in' let right = d.left ? d.target : d.source; @@ -2112,22 +1576,26 @@ var QDR = (function(QDR) { if (rightIndex >= 0) { let conn = onode['connection'].results[rightIndex]; conn = QDRService.utilities.flatten(onode['connection'].attributeNames, conn); - if ($scope.legend.status.optionsOpen && traffic) { - HTML = traffic.connectionPopupHTML(onode, conn, d); - if (HTML) - return HTML; - else - HTML = ''; + let conns = getConnsArray(d, conn); + if (conns.length === 1) { + HTML += '<h5>Connection'+(conns.length > 1 ? 's' : '')+'</h5>'; + HTML += '<table class="popupTable"><tr class="header"><td>Security</td><td>Authentication</td><td>Tenant</td><td>Host</td>'; + + for (let c=0; c<conns.length; c++) { + HTML += ('<tr><td>' + connSecurity(conns[c]) + '</td>'); + HTML += ('<td>' + connAuth(conns[c]) + '</td>'); + HTML += ('<td>' + (connTenant(conns[c]) || '--') + '</td>'); + HTML += ('<td>' + conns[c].host + '</td>'); + HTML += '</tr>'; + } + HTML += '</table>'; } - HTML += '<table class="popupTable">'; - HTML += ('<tr><td>Security</td><td>' + connSecurity(conn) + '</td></tr>'); - HTML += ('<tr><td>Authentication</td><td>' + connAuth(conn) + '</td></tr>'); - HTML += ('<tr><td>Tenant</td><td>' + connTenant(conn) + '</td></tr>'); - HTML += '</table>'; + HTML += linksHTML(onode, conn, d); } return HTML; }; + animate = true; setupInitialUpdate(); QDRService.management.topology.startUpdating(false); http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/e149d593/console/stand-alone/plugin/js/topology/traffic.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/js/topology/traffic.js b/console/stand-alone/plugin/js/topology/traffic.js index e061000..e52df67 100644 --- a/console/stand-alone/plugin/js/topology/traffic.js +++ b/console/stand-alone/plugin/js/topology/traffic.js @@ -66,9 +66,6 @@ Traffic.prototype.setAnimationType = function (type, converter, radius) { Traffic.prototype.doUpdate = function () { this.vis.doUpdate(); }; -Traffic.prototype.connectionPopupHTML = function (onode, conn, d) { - return this.vis.connectionPopupHTML(onode, conn, d); -}; /* Base class for congestion and dots visualizations */ function TrafficAnimation (traffic) { @@ -82,9 +79,6 @@ TrafficAnimation.prototype.nodeIndexFor = function (nodes, name) { } return -1; }; -TrafficAnimation.prototype.connectionPopupHTML = function () { - return null; -}; /* Color the links between router to show how heavily used the links are. */ function Congestion (traffic) { @@ -208,97 +202,6 @@ Congestion.prototype.remove = function () { .selectAll('marker').remove(); }; -// construct HTML to be used in a popup when the mouse is moved over a link. -// The HTML is sanitized elsewhere before it is displayed -Congestion.prototype.connectionPopupHTML = function (onode, conn, d) { - const max_links = 10; - const fields = ['undelivered', 'unsettled', 'rejected', 'released', 'modified']; - // local function to determine if a link's connectionId is in any of the connections - let isLinkFor = function (connectionId, conns) { - for (let c=0; c<conns.length; c++) { - if (conns[c].identity === connectionId) - return true; - } - return false; - }; - let fnJoin = function (ar, sepfn) { - let out = ''; - out = ar[0]; - for (let i=1; i<ar.length; i++) { - let sep = sepfn(ar[i]); - out += (sep[0] + sep[1]); - } - return out; - }; - let conns = [conn]; - // if the data for the line is from a client (small circle), we may have multiple connections - if (d.cls === 'small') { - conns = []; - let normals = d.target.normals ? d.target.normals : d.source.normals; - for (let n=0; n<normals.length; n++) { - if (normals[n].resultIndex !== undefined) { - conns.push(this.traffic.QDRService.utilities.flatten(onode['connection'].attributeNames, - onode['connection'].results[normals[n].resultIndex])); - } - } - } - // loop through all links for this router and accumulate those belonging to the connection(s) - let nodeLinks = onode['router.link']; - let links = []; - let hasAddress = false; - for (let n=0; n<nodeLinks.results.length; n++) { - let link = this.traffic.QDRService.utilities.flatten(nodeLinks.attributeNames, nodeLinks.results[n]); - if (link.linkType !== 'router-control') { - if (isLinkFor(link.connectionId, conns)) { - if (link.owningAddr) - hasAddress = true; - links.push(link); - } - } - } - // we may need to limit the number of links displayed, so sort descending by the sum of the field values - links.sort( function (a, b) { - let asum = a.undeliveredCount + a.unsettledCount + a.rejectedCount + a.releasedCount + a.modifiedCount; - let bsum = b.undeliveredCount + b.unsettledCount + b.rejectedCount + b.releasedCount + b.modifiedCount; - return asum < bsum ? 1 : asum > bsum ? -1 : 0; - }); - let HTMLHeading = '<h4>Links</h4>'; - let HTML = '<table class="popupTable">'; - // copy of fields since we may be prepending an address - let th = fields.slice(); - // convert to actual attribute names - let td = fields.map( function (f) {return f + 'Count';}); - th.unshift('dir'); - td.unshift('linkDir'); - // add an address field if any of the links had an owningAddress - if (hasAddress) { - th.unshift('address'); - td.unshift('owningAddr'); - } - // add rows to the table for each link - HTML += ('<tr><td>' + th.join('</td><td>') + '</tr></td>'); - for (let l=0; l<links.length; l++) { - if (l>=max_links) { - HTMLHeading = '<h4>Top ' + max_links + ' Links</h4>'; - break; - } - let link = links[l]; - let vals = td.map( function (f) { - if (f === 'owningAddr') { - let identity = this.traffic.QDRService.utilities.identity_clean(link.owningAddr); - return this.traffic.QDRService.utilities.addr_text(identity); - } - return link[f]; - }.bind(this)); - let joinedVals = fnJoin(vals, function (v1) { - return ['</td><td' + (isNaN(+v1) ? '': ' align="right"') + '>', this.traffic.QDRService.utilities.pretty(v1 || '0')]; - }.bind(this)); - HTML += ('<tr><td>' + joinedVals + '</td></tr>'); - } - HTML += '</table>'; - return HTMLHeading + HTML; -}; - /* Create animated dots moving along the links between routers to show message flow */ function Dots (traffic, converter, radius) { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
