DISPATCH-1195 Periodically update popup detail 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/55b7ae55 Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/55b7ae55 Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/55b7ae55 Branch: refs/heads/master Commit: 55b7ae55e4f4c2f89f25a928fdd0e98049372f5e Parents: 3adde8e Author: Ernest Allen <[email protected]> Authored: Mon Dec 3 10:48:50 2018 -0500 Committer: Ernest Allen <[email protected]> Committed: Mon Dec 3 10:48:50 2018 -0500 ---------------------------------------------------------------------- console/.eslintrc.json | 53 +- console/stand-alone/main.js | 2 +- console/stand-alone/plugin/css/dispatch.css | 3 + .../plugin/html/tmplClientDetail.html | 258 +++++--- console/stand-alone/plugin/js/amqp/topology.js | 434 +++++++------ console/stand-alone/plugin/js/amqp/utilities.js | 8 +- console/stand-alone/plugin/js/chord/data.js | 3 + .../plugin/js/dlgDetailController.js | 207 +++++-- console/stand-alone/plugin/js/topology/links.js | 345 +++++------ console/stand-alone/plugin/js/topology/map.js | 4 + console/stand-alone/plugin/js/topology/nodes.js | 416 ++++++++----- .../plugin/js/topology/qdrTopology.js | 79 ++- .../stand-alone/plugin/js/topology/topoUtils.js | 112 ++-- .../stand-alone/plugin/js/topology/traffic.js | 604 +++++++++++-------- console/stand-alone/test/links.js | 12 +- console/stand-alone/tslint.json | 18 +- 16 files changed, 1470 insertions(+), 1088 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/console/.eslintrc.json ---------------------------------------------------------------------- diff --git a/console/.eslintrc.json b/console/.eslintrc.json index 64d913c..f9c80bb 100644 --- a/console/.eslintrc.json +++ b/console/.eslintrc.json @@ -1,35 +1,22 @@ { - "env": { - "browser": true, - "node": true, - "amd": true, - "jquery": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 6, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true - } - }, - "rules": { - "indent": [ - "error", - 2 - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "always" - ], - "no-console": "off" + "env": { + "browser": true, + "node": true, + "amd": true, + "jquery": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true } -} \ No newline at end of file + }, + "rules": { + "indent": ["error", 2], + "linebreak-style": ["error", "unix"], + "semi": ["error", "always"], + "no-console": "off" + } +} http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/console/stand-alone/main.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/main.js b/console/stand-alone/main.js index a02b87a..4cf2d32 100644 --- a/console/stand-alone/main.js +++ b/console/stand-alone/main.js @@ -90,7 +90,7 @@ import { posint } from './plugin/js/posintDirective.js'; QDR.module.filter('to_trusted', ['$sce', function($sce){ return function(text) { - return $sce.trustAsHtml(text); + return $sce.trustAsHtml(text+''); }; }]); http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/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 000b961..6b0ef2e 100644 --- a/console/stand-alone/plugin/css/dispatch.css +++ b/console/stand-alone/plugin/css/dispatch.css @@ -1064,6 +1064,9 @@ svg { circle.node.inter-router { fill: #EAEAEA; } + circle.node.normal { + fill: #FAFAFA; + } circle.node.normal.in { fill: #F0F000; } http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/console/stand-alone/plugin/html/tmplClientDetail.html ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/html/tmplClientDetail.html b/console/stand-alone/plugin/html/tmplClientDetail.html index b2ef1f9..d96cda3 100644 --- a/console/stand-alone/plugin/html/tmplClientDetail.html +++ b/console/stand-alone/plugin/html/tmplClientDetail.html @@ -46,6 +46,64 @@ div.details span.right { float: right; } + + div.sub-table { + border: 1px solid #CCCCCC; + margin: 1em 0; + } + div.sub-table-row.body { + background-color: #FFFFFF; + } + div.sub-table-row { + border-bottom: 1px solid #CCCCCC; + } + div.sub-table-row:last-child { + border-bottom: 0px; + } + div.sub-table-row.header { + background-clip: padding-box; + background-color: #f5f5f5; + background-image: linear-gradient(to bottom,#fafafa 0,#ededed 100%); + background-repeat: repeat-x; + } + + span.sub-table-col { + display: inline-block; + border-right: 1px solid #CCCCCC; + padding: 2px 10px 3px; + } + span.sub-table-col:last-child { + border-right: 0px; + } + + + dl.sub-table { + display: grid; + grid-template-columns: max-content auto; + margin-left: 0.5em; + background-color: white; + border: 1px solid #CCCCCC; + } + + dl.sub-table dt { + grid-column-start: 1; + padding: 3px 3em 3px 10px; + border-bottom: 1px solid #CCCCCC; + border-right: 1px solid #CCCCCC; + } + + dl.sub-table dd { + grid-column-start: 2; + padding: 3px 10px; + border-bottom: 1px solid #CCCCCC; + } + dl.sub-table dd:last-child, dl.sub-table dt:last-of-type { + border-bottom: 0px; + } + + dl.sub-table dd.odd, dl.sub-table dt.odd { + background-color: #f0f0f0; + } </style> <!-- This is the template for the client detail popup displayed when a group @@ -78,47 +136,47 @@ <th>Links</th> </tr> </thead> - <tr ng-repeat-start="(key, value) in detail.infoPerId" + <tr ng-repeat-start="info in detail.infoPerId" ng-class="{even: $even, odd: $odd}" - ng-click="expandClicked(key)"> + ng-click="expandClicked(info.container)"> <td class="expander"> <span class="fa" - ng-class="expanded(key) ? 'fa-angle-down' : 'fa-angle-right'" + ng-class="expanded(info.container) ? 'fa-angle-down' : 'fa-angle-right'" ></span> </td> - <td>{{key}}</td><!-- Id --> - <td class="right">{{value.encrypted}}</td> - <td class="right">{{value.host}}</td> - <td class="right">{{value.linkCount}}</td> + <td>{{info.container}}</td><!-- Id --> + <td class="right">{{info.encrypted}}</td> + <td class="right">{{info.host}}</td> + <td class="right">{{info.linkCount}}</td> </tr> <tr ng-repeat-end - ng-class="{hiddenRow: !expanded(key)}" - ng-click="expandClicked(key)" + ng-class="{hiddenRow: !expanded(info.container)}" + ng-click="expandClicked(info.container)" > <td colspan="6"> - <table class="table table-striped table-bordered dataTable no-footer"> - <thead> - <tr> - <td ng-repeat="field in linkFields"> - {{field | humanify}} - </td> - </tr> - </thead> - <tbody> - <tr ng-repeat="link in value.links"> - <td ng-repeat="field in linkFields"> - {{link[field] | pretty }} - </td> - </tr> - </tbody> - </table> + <div class="sub-table"> + <div class="sub-table-row header"> + <span class="sub-table-col client" + ng-style="{ width: fieldWidth(field, info.sizes) }" + ng-repeat="field in fields.linkFields.cols"> + {{field | humanify}} + </span> + </div> + <div class="sub-table-row body" ng-repeat="link in info.links"> + <span class="sub-table-col client" + ng-style="{ width: fieldWidth(field, info.sizes) }" + ng-repeat="field in fields.linkFields.cols"> + {{link[field] | pretty}} + </span> + </div> + </div> </td> </tr> </table> </script> <script type="text/ng-template" id="consoles.html"> - here be console info + </script> <script type="text/ng-template" id="edgeRouters.html"> @@ -134,84 +192,94 @@ <th>Accepted</td> </tr> </thead> - <tr ng-repeat-start="(key, value) in detail.infoPerId" + <tr ng-repeat-start="info in detail.infoPerId" ng-class="{even: $even, odd: $odd}" - ng-click="expandClicked(key)"> + ng-click="expandClicked(info.name)"> <td class="expander"> <span class="fa" - ng-class="expanded(key) ? 'fa-angle-down' : 'fa-angle-right'" + ng-class="expanded(info.name) ? 'fa-angle-down' : 'fa-angle-right'" ></span> </td> - <td>{{key}}</td><!-- Id --> - <td class="right">{{value.linkRouteCount}}</td> - <td class="right">{{value.autoLinkCount}}</td> - <td class="right">{{value.connectionCount}}</td> - <td class="right">{{value.addrCount}}</td> - <td class="right">{{value.acceptedDeliveries | pretty}}</td> + <td>{{info.name}}</td><!-- Id --> + <td class="right">{{info.linkRouteCount}}</td> + <td class="right">{{info.autoLinkCount}}</td> + <td class="right">{{info.connectionCount}}</td> + <td class="right">{{info.addrCount}}</td> + <td class="right">{{info.acceptedDeliveries | pretty}}</td> </tr> <tr ng-repeat-end - ng-class="{hiddenRow: !expanded(key)}" - ng-click="expandClicked(key)" + ng-class="{hiddenRow: !expanded(info.name)}" + ng-click="expandClicked(info.name)" > <td colspan="7"> - <h4>Edge router details</h4> - <table class="table table-striped table-bordered dataTable no-footer"> - <tr ng-repeat="field in detailFields"> - <td>{{field | humanify}}</td> - <td>{{value[field] | pretty}}</td> - </tr> - </table> - <h4>Link routes</h4> - <table class="table table-striped table-bordered dataTable no-footer"> - <thead> - <tr> - <td ng-repeat="field in linkRouteFields"> - {{field | humanify}} - </td> - </tr> - </thead> - <tbody> - <tr ng-repeat="link in value.linkRoutes"> - <td ng-repeat="field in linkRouteFields"> - {{link[field] | pretty }} - </td> - </tr> - </tbody> - </table> - <h4>Autolinks</h4> - <table class="table table-striped table-bordered dataTable no-footer"> - <thead> - <tr> - <td ng-repeat="field in autoLinkFields"> - {{field | humanify}} - </td> - </tr> - </thead> - <tbody> - <tr ng-repeat="link in value.autoLinks"> - <td ng-repeat="field in autoLinkFields"> - {{link[field] | pretty }} - </td> - </tr> - </tbody> - </table> - <h4>Addresses</h4> - <table class="table table-striped table-bordered dataTable no-footer"> - <thead> - <tr> - <td ng-repeat="field in addressFields"> - {{field | humanify}} - </td> - </tr> - </thead> - <tbody> - <tr ng-repeat="link in value.addresses"> - <td ng-repeat="field in addressFields"> - {{link[field] | pretty }} - </td> - </tr> - </tbody> - </table> + <h4>Details for edge router {{info.name}}</h4> + <dl class="sub-table"> + <dt ng-repeat-start="field in fields.detailFields.cols"> + {{field | humanify}} + </dt> + <dd ng-repeat-end> + {{info[field] | pretty}} + </dd> + </dl> + + <h4>Link routes</h4> + <div class="sub-table"> + <div class="sub-table-row header"> + <span + class="sub-table-col client" + ng-style="{ width: fieldWidth(field, info.linkRouteSizes) }" + ng-repeat="field in fields.linkRouteFields.cols"> + {{field | humanify}} + </span> + </div> + <div class="sub-table-row body" ng-repeat="link in info.linkRoutes"> + <span + class="sub-table-col client" + ng-style="{ width: fieldWidth(field, info.linkRouteSizes) }" + ng-repeat="field in fields.linkRouteFields.cols"> + {{link[field] | pretty}} + </span> + </div> + </div> + + <h4>Autolinks</h4> + <div class="sub-table"> + <div class="sub-table-row header"> + <span + class="sub-table-col client" + ng-style="{ width: fieldWidth(field, info.autoLinkSizes) }" + ng-repeat="field in fields.autoLinkFields.cols"> + {{field | humanify}} + </span> + </div> + <div class="sub-table-row body" ng-repeat="link in info.autoLinks"> + <span + class="sub-table-col client" + ng-style="{ width: fieldWidth(field, info.autoLinkSizes) }" + ng-repeat="field in fields.autoLinkFields.cols"> + {{link[field] | pretty}} + </span> + </div> + </div> + <h4>Addresses</h4> + <div class="sub-table"> + <div class="sub-table-row header"> + <span + class="sub-table-col client" + ng-style="{ width: fieldWidth(field, info.addressSizes) }" + ng-repeat="field in fields.addressFields.cols"> + {{field | humanify}} + </span> + </div> + <div class="sub-table-row body" ng-repeat="link in info.addresses"> + <span + class="sub-table-col client" + ng-style="{ width: fieldWidth(field, info.addressSizes) }" + ng-repeat="field in fields.addressFields.cols"> + {{link[field] | pretty}} + </span> + </div> + </div> </td> </tr> </table> http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/console/stand-alone/plugin/js/amqp/topology.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/js/amqp/topology.js b/console/stand-alone/plugin/js/amqp/topology.js index 8c88e50..02f1fe9 100644 --- a/console/stand-alone/plugin/js/amqp/topology.js +++ b/console/stand-alone/plugin/js/amqp/topology.js @@ -14,9 +14,9 @@ * limitations under the License. */ -/* global Promise d3 */ +/* global Promise d3 Set */ -import { utils } from './utilities.js'; +import { utils } from "./utilities.js"; class Topology { constructor(connectionManager) { @@ -32,13 +32,12 @@ class Topology { this.updating = false; } addUpdatedAction(key, action) { - if (typeof action === 'function') { + if (typeof action === "function") { this.updatedActions[key] = action; } } delUpdatedAction(key) { - if (key in this.updatedActions) - delete this.updatedActions[key]; + if (key in this.updatedActions) delete this.updatedActions[key]; } executeUpdatedActions(error) { for (var action in this.updatedActions) { @@ -52,7 +51,7 @@ class Topology { } } addUpdateEntities(entityAttribs) { - if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') { + if (Object.prototype.toString.call(entityAttribs) !== "[object Array]") { entityAttribs = [entityAttribs]; } for (var i = 0; i < entityAttribs.length; i++) { @@ -61,12 +60,10 @@ class Topology { } } on(eventName, fn, key) { - if (eventName === 'updated') - this.addUpdatedAction(key, fn); + if (eventName === "updated") this.addUpdatedAction(key, fn); } unregister(eventName, key) { - if (eventName === 'updated') - this.delUpdatedAction(key); + if (eventName === "updated") this.delUpdatedAction(key); } nodeInfo() { return this._nodeInfo; @@ -76,14 +73,17 @@ class Topology { for (let rId in this._nodeInfo) { if (!workSet.has(rId)) { // mark any routers that went away since the last request as removed - this._nodeInfo[rId]['removed'] = true; + this._nodeInfo[rId]["removed"] = true; } else { - if (this._nodeInfo[rId]['removed']) - delete this._nodeInfo[rId]['removed']; + if (this._nodeInfo[rId]["removed"]) + delete this._nodeInfo[rId]["removed"]; // copy entities for (let entity in workInfo[rId]) { - if (!this._nodeInfo[rId][entity] || - (workInfo[rId][entity]['timestamp']+'' > this._nodeInfo[rId][entity]['timestamp']+'')) { + if ( + !this._nodeInfo[rId][entity] || + workInfo[rId][entity]["timestamp"] + "" > + this._nodeInfo[rId][entity]["timestamp"] + "" + ) { this._nodeInfo[rId][entity] = utils.copy(workInfo[rId][entity]); } } @@ -97,111 +97,148 @@ class Topology { } } } + // remove any nodes that don't have connection info + purge() { + for (let id in this._nodeInfo) { + let node = this._nodeInfo[id]; + if (node.removed) { + delete this._nodeInfo[id]; + } + } + } get() { - return new Promise((function (resolve, reject) { - this.connection.sendMgmtQuery('GET-MGMT-NODES') - .then((function (results) { - let routerIds = results.response; - if (Object.prototype.toString.call(routerIds) === '[object Array]') { - // if there is only one node, it will not be returned - if (routerIds.length === 0) { - var parts = this.connection.getReceiverAddress().split('/'); - parts[parts.length - 1] = '$management'; - routerIds.push(parts.join('/')); - } - let finish = function (workInfo) { - this.saveResults(workInfo); - this.onDone(this._nodeInfo); - resolve(this._nodeInfo); - }; - let connectedToEdge = function (response, workInfo) { - let routerId = null; - if (response.length === 1) { - let parts = response[0].split('/'); - // we are connected to an edge router - if (parts[1] === '_edge') { - // find the role:edge connection - let conn = workInfo[response[0]].connection; - if (conn) { - let roleIndex = conn.attributeNames.indexOf('role'); - for (let i=0; i<conn.results.length; i++) { - if (conn.results[i][roleIndex] === 'edge') { - let container = utils.valFor(conn.attributeNames, conn.results[i], 'container'); - return utils.idFromName(container, '_topo'); - } - } - } - } + return new Promise( + function(resolve, reject) { + this.connection.sendMgmtQuery("GET-MGMT-NODES").then( + function(results) { + let routerIds = results.response; + if ( + Object.prototype.toString.call(routerIds) === "[object Array]" + ) { + // if there is only one node, it will not be returned + if (routerIds.length === 0) { + var parts = this.connection.getReceiverAddress().split("/"); + parts[parts.length - 1] = "$management"; + routerIds.push(parts.join("/")); } - return routerId; - }; - this.doget(routerIds) - .then( function (workInfo) { - // test for edge case - let routerId = connectedToEdge(routerIds, workInfo); - if (routerId) { - this.connection.sendMgmtQuery('GET-MGMT-NODES', routerId) - .then((function (results) { - let response = results.response; - if (Object.prototype.toString.call(response) === '[object Array]') { - // special case of edge case: - // we are connected to an edge router that is connected to - // a router that is not connected to any other interior routers - if (response.length === 0) { - response = [routerId]; + let finish = function(workInfo) { + this.saveResults(workInfo); + this.onDone(this._nodeInfo); + resolve(this._nodeInfo); + }; + let connectedToEdge = function(response, workInfo) { + let routerId = null; + if (response.length === 1) { + let parts = response[0].split("/"); + // we are connected to an edge router + if (parts[1] === "_edge") { + // find the role:edge connection + let conn = workInfo[response[0]].connection; + if (conn) { + let roleIndex = conn.attributeNames.indexOf("role"); + for (let i = 0; i < conn.results.length; i++) { + if (conn.results[i][roleIndex] === "edge") { + let container = utils.valFor( + conn.attributeNames, + conn.results[i], + "container" + ); + return utils.idFromName(container, "_topo"); } - this.doget(response) - .then( function (workInfo) { - finish.call(this, workInfo); - }.bind(this)); - } - }).bind(this)); - } else { - finish.call(this, workInfo); + } + } } - }.bind(this)); + return routerId; + }; + this.doget(routerIds).then( + function(workInfo) { + // test for edge case + let routerId = connectedToEdge(routerIds, workInfo); + if (routerId) { + this.connection + .sendMgmtQuery("GET-MGMT-NODES", routerId) + .then( + function(results) { + let response = results.response; + if ( + Object.prototype.toString.call(response) === + "[object Array]" + ) { + // special case of edge case: + // we are connected to an edge router that is connected to + // a router that is not connected to any other interior routers + if (response.length === 0) { + response = [routerId]; + } + this.doget(response).then( + function(workInfo) { + finish.call(this, workInfo); + }.bind(this) + ); + } + }.bind(this) + ); + } else { + finish.call(this, workInfo); + } + }.bind(this) + ); + } + }.bind(this), + function(error) { + reject(error); } - }).bind(this), function (error) { - reject(error); - }); - }).bind(this)); + ); + }.bind(this) + ); } doget(ids) { - return new Promise((function (resolve) { - let workInfo = {}; - for (var i = 0; i < ids.length; ++i) { - workInfo[ids[i]] = {}; - } - var gotResponse = function (nodeName, entity, response) { - workInfo[nodeName][entity] = response; - workInfo[nodeName][entity]['timestamp'] = new Date(); - }; - var q = d3.queue(this.connection.availableQeueuDepth()); - for (var id in workInfo) { - for (var entity in this.entityAttribs) { - q.defer((this.q_fetchNodeInfo).bind(this), id, entity, this.entityAttribs[entity], q, gotResponse); + return new Promise( + function(resolve) { + let workInfo = {}; + for (var i = 0; i < ids.length; ++i) { + workInfo[ids[i]] = {}; } - } - q.await((function () { - // filter out nodes that have no connection info - if (this.filtering) { - for (var id in workInfo) { - if (!(workInfo[id].connection)) { - this.flux = true; - delete workInfo[id]; - } + var gotResponse = function(nodeName, entity, response) { + workInfo[nodeName][entity] = response; + workInfo[nodeName][entity]["timestamp"] = new Date(); + }; + var q = d3.queue(this.connection.availableQeueuDepth()); + for (var id in workInfo) { + for (var entity in this.entityAttribs) { + q.defer( + this.q_fetchNodeInfo.bind(this), + id, + entity, + this.entityAttribs[entity], + q, + gotResponse + ); } } - resolve(workInfo); - }).bind(this)); - }).bind(this)); + q.await( + function() { + // filter out nodes that have no connection info + if (this.filtering) { + for (var id in workInfo) { + if (!workInfo[id].connection) { + this.flux = true; + delete workInfo[id]; + } + } + } + resolve(workInfo); + }.bind(this) + ); + }.bind(this) + ); } onDone(result) { clearTimeout(this._getTimer); if (this.updating) - this._getTimer = setTimeout((this.get).bind(this), this.updateInterval); + this._getTimer = setTimeout(this.get.bind(this), this.updateInterval); this.executeUpdatedActions(result); } startUpdating(filter) { @@ -219,18 +256,29 @@ class Topology { } fetchEntity(node, entity, attrs, callback) { var results = {}; - var gotResponse = function (nodeName, dotentity, response) { + var gotResponse = function(nodeName, dotentity, response) { results = response; }; var q = d3.queue(this.connection.availableQeueuDepth()); - q.defer((this.q_fetchNodeInfo).bind(this), node, entity, attrs, q, gotResponse); - q.await(function () { + q.defer( + this.q_fetchNodeInfo.bind(this), + node, + entity, + attrs, + q, + gotResponse + ); + q.await(function() { callback(node, entity, results); }); } // called from d3.queue.defer so the last argument (callback) is supplied by d3 q_fetchNodeInfo(nodeId, entity, attrs, q, heartbeat, callback) { - this.getNodeInfo(nodeId, entity, attrs, q, function (nodeName, dotentity, response) { + this.getNodeInfo(nodeId, entity, attrs, q, function( + nodeName, + dotentity, + response + ) { heartbeat(nodeName, dotentity, response); callback(null); }); @@ -240,23 +288,29 @@ class Topology { var q = d3.queue(this.connection.availableQeueuDepth()); var results = {}; if (!resultCallback) { - resultCallback = function (nodeName, dotentity, response) { - if (!results[nodeName]) - results[nodeName] = {}; + resultCallback = function(nodeName, dotentity, response) { + if (!results[nodeName]) results[nodeName] = {}; results[nodeName][dotentity] = response; }; } - var gotAResponse = function (nodeName, dotentity, response) { + var gotAResponse = function(nodeName, dotentity, response) { resultCallback(nodeName, dotentity, response); }; - if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') { + if (Object.prototype.toString.call(entityAttribs) !== "[object Array]") { entityAttribs = [entityAttribs]; } for (var i = 0; i < entityAttribs.length; ++i) { var ea = entityAttribs[i]; - q.defer((this.q_fetchNodeInfo).bind(this), node, ea.entity, ea.attrs || [], q, gotAResponse); + q.defer( + this.q_fetchNodeInfo.bind(this), + node, + ea.entity, + ea.attrs || [], + q, + gotAResponse + ); } - q.await(function () { + q.await(function() { doneCallback(results); }); } @@ -265,44 +319,56 @@ class Topology { var q = d3.queue(this.connection.availableQeueuDepth()); var results = {}; if (!resultCallback) { - resultCallback = function (nodeName, dotentity, response) { - if (!results[nodeName]) - results[nodeName] = {}; + resultCallback = function(nodeName, dotentity, response) { + if (!results[nodeName]) results[nodeName] = {}; results[nodeName][dotentity] = response; }; } - var gotAResponse = function (nodeName, dotentity, response) { + var gotAResponse = function(nodeName, dotentity, response) { resultCallback(nodeName, dotentity, response); }; - if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') { + if (Object.prototype.toString.call(entityAttribs) !== "[object Array]") { entityAttribs = [entityAttribs]; } var nodes = Object.keys(this._nodeInfo); for (var n = 0; n < nodes.length; ++n) { for (var i = 0; i < entityAttribs.length; ++i) { var ea = entityAttribs[i]; - q.defer((this.q_fetchNodeInfo).bind(this), nodes[n], ea.entity, ea.attrs || [], q, gotAResponse); + q.defer( + this.q_fetchNodeInfo.bind(this), + nodes[n], + ea.entity, + ea.attrs || [], + q, + gotAResponse + ); } } - q.await(function () { + q.await(function() { doneCallback(results); }); } // enusre all the topology nones have all these entities ensureAllEntities(entityAttribs, callback, extra) { - this.ensureEntities(Object.keys(this._nodeInfo), entityAttribs, callback, extra); + this.ensureEntities( + Object.keys(this._nodeInfo), + entityAttribs, + callback, + extra + ); } // ensure these nodes have all these entities. don't fetch unless forced to ensureEntities(nodes, entityAttribs, callback, extra) { - if (Object.prototype.toString.call(nodes) !== '[object Array]') { + if (Object.prototype.toString.call(nodes) !== "[object Array]") { nodes = [nodes]; } this.addUpdateEntities(entityAttribs); - this.doget(nodes) - .then( function (results) { + this.doget(nodes).then( + function(results) { this.saveResults(results); callback(extra, results); - }.bind(this)); + }.bind(this) + ); } addNodeInfo(id, entity, values) { // save the results in the nodeInfo object @@ -312,7 +378,7 @@ class Topology { } // copy the values to allow garbage collection this._nodeInfo[id][entity] = values; - this._nodeInfo[id][entity]['timestamp'] = new Date(); + this._nodeInfo[id][entity]["timestamp"] = new Date(); } } isLargeNetwork() { @@ -321,10 +387,9 @@ class Topology { getConnForLink(link) { // find the connection for this link var conns = this._nodeInfo[link.nodeId].connection; - if (!conns) - return {}; - var connIndex = conns.attributeNames.indexOf('identity'); - var linkCons = conns.results.filter(function (conn) { + if (!conns) return {}; + var connIndex = conns.attributeNames.indexOf("identity"); + var linkCons = conns.results.filter(function(conn) { return conn[connIndex] === link.connectionId; }); return utils.flatten(conns.attributeNames, linkCons[0]); @@ -356,44 +421,71 @@ class Topology { } // d3.queue'd function to make a management query for entities/attributes q_ensureNodeInfo(nodeId, entity, attrs, q, callback) { - this.getNodeInfo(nodeId, entity, attrs, q, (function (nodeName, dotentity, response) { - this.addNodeInfo(nodeName, dotentity, response); - callback(null); - }).bind(this)); + this.getNodeInfo( + nodeId, + entity, + attrs, + q, + function(nodeName, dotentity, response) { + this.addNodeInfo(nodeName, dotentity, response); + callback(null); + }.bind(this) + ); return { - abort: function () { + abort: function() { delete this._nodeInfo[nodeId]; } }; } getNodeInfo(nodeName, entity, attrs, q, callback) { - var timedOut = function (q) { + var timedOut = function(q) { q.abort(); }; var atimer = setTimeout(timedOut, this.timeout, q); - this.connection.sendQuery(nodeName, entity, attrs) - .then(function (response) { + this.connection.sendQuery(nodeName, entity, attrs).then( + function(response) { clearTimeout(atimer); callback(nodeName, entity, response.response); - }, function () { + }, + function() { q.abort(); - }); - } - getMultipleNodeInfo(nodeNames, entity, attrs, callback, selectedNodeId, aggregate) { + } + ); + } + getMultipleNodeInfo( + nodeNames, + entity, + attrs, + callback, + selectedNodeId, + aggregate + ) { var self = this; - if (typeof aggregate === 'undefined') - aggregate = true; + if (typeof aggregate === "undefined") aggregate = true; var responses = {}; - var gotNodesResult = function (nodeName, dotentity, response) { + var gotNodesResult = function(nodeName, dotentity, response) { responses[nodeName] = response; }; var q = d3.queue(this.connection.availableQeueuDepth()); - nodeNames.forEach(function (id) { - q.defer((self.q_fetchNodeInfo).bind(self), id, entity, attrs, q, gotNodesResult); + nodeNames.forEach(function(id) { + q.defer( + self.q_fetchNodeInfo.bind(self), + id, + entity, + attrs, + q, + gotNodesResult + ); }); - q.await(function () { + q.await(function() { if (aggregate) - self.aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback); + self.aggregateNodeInfo( + nodeNames, + entity, + selectedNodeId, + responses, + callback + ); else { callback(nodeNames, entity, responses); } @@ -401,10 +493,15 @@ class Topology { } quiesceLink(nodeId, name) { var attributes = { - adminStatus: 'disabled', + adminStatus: "disabled", name: name }; - return this.connection.sendMethod(nodeId, 'router.link', attributes, 'UPDATE'); + return this.connection.sendMethod( + nodeId, + "router.link", + attributes, + "UPDATE" + ); } aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback) { // aggregate the responses @@ -420,7 +517,7 @@ class Topology { var result = thisNode.results[i]; var vals = []; // there is a val for each attribute in this entity - result.forEach(function (val) { + result.forEach(function(val) { vals.push({ sum: val, detail: [] @@ -428,25 +525,24 @@ class Topology { }); newResponse.aggregates.push(vals); } - var nameIndex = thisNode.attributeNames.indexOf('name'); + var nameIndex = thisNode.attributeNames.indexOf("name"); var ent = self.connection.schema.entityTypes[entity]; var ids = Object.keys(responses); ids.sort(); - ids.forEach(function (id) { + ids.forEach(function(id) { var response = responses[id]; var results = response.results; - results.forEach(function (result) { + results.forEach(function(result) { // find the matching result in the aggregates - var found = newResponse.aggregates.some(function (aggregate) { + var found = newResponse.aggregates.some(function(aggregate) { if (aggregate[nameIndex].sum === result[nameIndex]) { // result and aggregate are now the same record, add the graphable values - newResponse.attributeNames.forEach(function (key, i) { + newResponse.attributeNames.forEach(function(key, i) { if (ent.attributes[key] && ent.attributes[key].graph) { - if (id != selectedNodeId) - aggregate[i].sum += result[i]; + if (id != selectedNodeId) aggregate[i].sum += result[i]; } aggregate[i].detail.push({ - node: utils.nameFromId(id) + ':', + node: utils.nameFromId(id) + ":", val: result[i] }); }); @@ -458,13 +554,15 @@ class Topology { // this attribute was not found in the aggregates yet // because it was not in the selectedNodeId's results var vals = []; - result.forEach(function (val) { + result.forEach(function(val) { vals.push({ sum: val, - detail: [{ - node: utils.nameFromId(id), - val: val - }] + detail: [ + { + node: utils.nameFromId(id), + val: val + } + ] }); }); newResponse.aggregates.push(vals); @@ -475,4 +573,4 @@ class Topology { } } -export default Topology; \ No newline at end of file +export default Topology; http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/console/stand-alone/plugin/js/amqp/utilities.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/js/amqp/utilities.js b/console/stand-alone/plugin/js/amqp/utilities.js index c092bf4..517b4e1 100644 --- a/console/stand-alone/plugin/js/amqp/utilities.js +++ b/console/stand-alone/plugin/js/amqp/utilities.js @@ -46,10 +46,14 @@ var utils = { }); return flat; }, - flattenAll: function (entity) { + flattenAll: function (entity, filter) { + if (!filter) + filter = function (e) {return e;}; let results = []; for (let i=0; i<entity.results.length; i++) { - results.push(this.flatten(entity.attributeNames, entity.results[i])); + let f = filter(this.flatten(entity.attributeNames, entity.results[i])); + if (f) + results.push(f); } return results; }, http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/console/stand-alone/plugin/js/chord/data.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/js/chord/data.js b/console/stand-alone/plugin/js/chord/data.js index 64a58fa..c1cd978 100644 --- a/console/stand-alone/plugin/js/chord/data.js +++ b/console/stand-alone/plugin/js/chord/data.js @@ -99,6 +99,9 @@ class ChordData { // eslint-disable-line no-unused-vars // each routers has a different order for the routers let ingressRouters = []; let routerNode = results[nodeId]['router.node']; + if (!routerNode) { + continue; + } let idIndex = routerNode.attributeNames.indexOf('id'); // ingressRouters is an array of router names in the same order that the ingressHistogram values will be in for (let i = 0; i < routerNode.results.length; i++) { http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/console/stand-alone/plugin/js/dlgDetailController.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/js/dlgDetailController.js b/console/stand-alone/plugin/js/dlgDetailController.js index 8efff28..0742f2a 100644 --- a/console/stand-alone/plugin/js/dlgDetailController.js +++ b/console/stand-alone/plugin/js/dlgDetailController.js @@ -27,44 +27,68 @@ export class DetailDialogController { $scope.detail = { template: 'loading.html', }; - $scope.detailFields = [ - 'version', - 'mode', - 'presettledDeliveries', - 'droppedPresettledDeliveries', - 'acceptedDeliveries', - 'rejectedDeliveries', - 'releasedDeliveries', - 'modifiedDeliveries', - 'deliveriesIngress', - 'deliveriesEgress', - 'deliveriesTransit', - 'deliveriesIngressRouteContainer', - 'deliveriesEgressRouteContainer' - ]; - $scope.linkFields = [ - 'linkType', - 'owningAddr', - 'priority', - 'acceptedCount', - 'unsettledCount' - ]; - $scope.linkRouteFields = [ - 'prefix', - 'direction', - 'containerId' - ]; - $scope.autoLinkFields = [ - 'addr', - 'direction', - 'containerId' - ]; - $scope.addressFields = [ - 'prefix', - 'distribution' - ]; + let countChars = function (ar) { + let count = 0; + ar.forEach( function (a) { + count += a.length; + }); + return count; + }; + + $scope.fields = { + detailFields: { + cols: [ + 'version', + 'mode', + 'presettledDeliveries', + 'droppedPresettledDeliveries', + 'acceptedDeliveries', + 'rejectedDeliveries', + 'releasedDeliveries', + 'modifiedDeliveries', + 'deliveriesIngress', + 'deliveriesEgress', + 'deliveriesTransit', + 'deliveriesIngressRouteContainer', + 'deliveriesEgressRouteContainer' + ] + }, + linkFields: { + cols: [ + 'linkType', + 'owningAddr', + 'priority', + 'acceptedCount', + 'unsettledCount' + ] + }, + linkRouteFields: { + cols: [ + 'prefix', + 'direction', + 'containerId' + ] + }, + autoLinkFields: { + cols: [ + 'addr', + 'direction', + 'containerId' + ] + }, + addressFields: { + cols: [ + 'prefix', + 'distribution' + ] + } + }; + for (let f in $scope.fields) { + $scope.fields[f].count = countChars($scope.fields[f].cols); + } $scope.okClick = function () { + clearInterval(updateTimer); $uibModalInstance.close(true); }; $scope.expandClicked = function (id) { @@ -78,6 +102,30 @@ export class DetailDialogController { $scope.expanded = function (id) { return expandedRows.has(id); }; + $scope.cellWidth = function (key, val) { + if (key === 'autoLinkFields') { + return val === 'addr' ? '40%' : '20%'; + } + let totalChars = $scope.fields[key].count; + return `${Math.round(val.length * 100 / totalChars)}%`; + }; + $scope.fieldWidth = function (val, sizes) { + if (!sizes) + return '10%'; + return `${Math.round(sizes[val] * 100 / sizes.total)}%`; + }; + let updateSizes = function (fields, sizes, obj) { + fields.forEach( function (key) { + if (!sizes[key]) + sizes[key] = QDRService.utilities.humanify(key).length; + sizes[key] = Math.max(sizes[key], QDRService.utilities.pretty(obj[key]).length); + }); + sizes.total = 0; + for (let key in sizes) { + if (key !== 'total') + sizes.total += sizes[key]; + } + }; let groupDetail = function () { let q_getEdgeInfo = function (n, infoPerId, callback) { @@ -101,15 +149,30 @@ export class DetailDialogController { let nodeId = QDRService.utilities.idFromName(id, '_edge'); QDRService.management.topology.fetchEntities(nodeId, [{entity: 'router.link', attrs: []}, - {entity: 'linkRoute', attrs: $scope.linkRouteFields}, - {entity: 'autoLink', attrs: $scope.autoLinkFields}, + {entity: 'linkRoute', attrs: $scope.fields.linkRouteFields.cols}, + {entity: 'autoLink', attrs: $scope.fields.autoLinkFields.cols}, {entity: 'address', attrs: []}, ], function (results) { $timeout( function () { - infoPerId[id].linkRoutes = QDRService.utilities.flattenAll(results[nodeId].linkRoute); - infoPerId[id].autoLinks = QDRService.utilities.flattenAll(results[nodeId].autoLink); - infoPerId[id].addresses = QDRService.utilities.flattenAll(results[nodeId].address); + infoPerId[id].linkRouteSizes = {}; + infoPerId[id].linkRoutes = QDRService.utilities.flattenAll(results[nodeId].linkRoute, + function (route) { + updateSizes($scope.fields.linkRouteFields.cols, infoPerId[id].linkRouteSizes, route); + return route; + }); + infoPerId[id].autoLinkSizes = {}; + infoPerId[id].autoLinks = QDRService.utilities.flattenAll(results[nodeId].autoLink, + function (link) { + updateSizes($scope.fields.autoLinkFields.cols, infoPerId[id].autoLinkSizes, link); + return link; + }); + infoPerId[id].addressSizes = {}; + infoPerId[id].addresses = QDRService.utilities.flattenAll(results[nodeId].address, + function (addr) { + updateSizes($scope.fields.addressFields.cols, infoPerId[id].addressSizes, addr); + return addr; + }); }); }); }; @@ -117,43 +180,44 @@ export class DetailDialogController { let q = d3.queue(10); for (let n=0; n<d.normals.length; n++) { q.defer(q_getEdgeInfo, d.normals[n], infoPerId); + if (expandedRows.has(d.normals[n].container)) { + $scope.detail.moreInfo(d.normals[n].container); + } } q.await(function () { $scope.detail.template = 'edgeRouters.html'; - $scope.detail.title = 'for edge router'; + $scope.detail.title = 'edge router'; resolve({ - description: 'Expand an edge router to see more info', + description: 'Select an edge router to see more info', infoPerId: infoPerId }); }); - } else if (d.isConsole) { - $scope.detail.template = 'consoles.html'; - $scope.detail.title = 'for console'; - resolve({ - description: '' - }); } else { $scope.detail.moreInfo = function () {}; + let attrs = QDRService.utilities.copy($scope.fields.linkFields.cols); + attrs.unshift('connectionId'); QDRService.management.topology.fetchEntities(d.key, - [{entity: 'router.link', attrs: []}], + [{entity: 'router.link', attrs: attrs}], function (results) { let links = results[d.key]['router.link']; for (let i=0; i<d.normals.length; i++) { let n = d.normals[i]; let conn = {}; - let connectionIndex = links.attributeNames.indexOf('connectionId'); infoPerId[n.container] = conn; conn.container = n.container; - conn.encrypted = n.encrypted; + conn.encrypted = n.encrypted ? 'True' : 'False'; conn.host = n.host; - conn.links = []; - for (let l=0; l<links.results.length; l++) { - if (links.results[l][connectionIndex] === n.connectionId) { - let link = QDRService.utilities.flatten(links.attributeNames, links.results[l]); + //conn.links = []; + conn.sizes = {}; + conn.links = QDRService.utilities.flattenAll(links, function (link) { + if (link.connectionId === n.connectionId) { link.owningAddr = QDRService.utilities.addr_text(link.owningAddr); - conn.links.push(link); + updateSizes($scope.fields.linkFields.cols, conn.sizes, link); + return link; + } else { + return null; } - } + }); conn.linkCount = conn.links.length; } let dir = d.cdir === 'in' ? 'inbound' : d.cdir === 'both' ? 'in and outbound' : 'outbound'; @@ -172,14 +236,23 @@ export class DetailDialogController { })); }; - groupDetail() - .then( function (det) { - $timeout( function () { - $scope.detail.title = `for ${d.normals.length} ${$scope.detail.title}${d.normals.length > 1 ? 's' : ''}`; - $scope.detail.description = det.description; - $scope.detail.infoPerId = det.infoPerId; - }, 10); - }); + let updateDetail = function () { + groupDetail() + .then( function (det) { + $timeout( function () { + $scope.detail.title = `for ${d.normals.length} ${$scope.detail.title}${d.normals.length > 1 ? 's' : ''}`; + $scope.detail.description = det.description; + $scope.detail.infoPerId = Object.keys(det.infoPerId).map( function (id) { + return det.infoPerId[id]; + }).sort( function (a, b) { + return a.name > b.name ? 1 : -1; + }); + }, 10); + }); + }; + let updateTimer = setInterval(updateDetail, 2000); + updateDetail(); + } } -DetailDialogController.$inject = ['QDRService', '$scope', '$timeout', '$uibModalInstance', 'd']; +DetailDialogController.$inject = ['QDRService', '$scope', '$timeout', '$uibModalInstance', 'd']; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/console/stand-alone/plugin/js/topology/links.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/js/topology/links.js b/console/stand-alone/plugin/js/topology/links.js index 264cb8c..16b086c 100644 --- a/console/stand-alone/plugin/js/topology/links.js +++ b/console/stand-alone/plugin/js/topology/links.js @@ -17,22 +17,27 @@ specific language governing permissions and limitations under the License. */ -import { utils } from '../amqp/utilities.js'; +import { utils } from "../amqp/utilities.js"; class Link { constructor(source, target, dir, cls, uid) { this.source = source; this.target = target; - this.left = (dir == 'in' || dir == 'both'); - this.right = (dir == 'out' || dir == 'both'); + this.left = dir == "in" || dir == "both"; + this.right = dir == "out" || dir == "both"; this.cls = cls; this.uid = uid; } - markerId (end) { - let selhigh = this.highlighted ? 'highlighted' : this.selected ? 'selected' : ''; - if (selhigh === '' && (!this.left && !this.right)) - selhigh = 'unknown'; - return `-${selhigh}-${end === 'end' ? this.target.radius() : this.source.radius()}`; + markerId(end) { + let selhigh = this.highlighted + ? "highlighted" + : this.selected + ? "selected" + : ""; + if (selhigh === "" && (!this.left && !this.right)) selhigh = "unknown"; + return `-${selhigh}-${ + end === "end" ? this.target.radius() : this.source.radius() + }`; } } @@ -41,10 +46,9 @@ export class Links { this.links = []; this.logger = logger; } - getLinkSource (nodesIndex) { - for (let i=0; i<this.links.length; ++i) { - if (this.links[i].target === nodesIndex) - return i; + getLinkSource(nodesIndex) { + for (let i = 0; i < this.links.length; ++i) { + if (this.links[i].target === nodesIndex) return i; } return -1; } @@ -52,7 +56,7 @@ export class Links { for (let i = 0; i < this.links.length; i++) { let s = this.links[i].source, t = this.links[i].target; - if (typeof this.links[i].source == 'object') { + if (typeof this.links[i].source == "object") { s = s.id; t = t.id; } @@ -65,119 +69,144 @@ export class Links { } } //this.logger.debug("creating new link (" + (links.length) + ") between " + nodes[_source].name + " and " + nodes[_target].name); - if (this.links.some( function (l) { return l.uid === uid;})) - uid = uid + '.' + this.links.length; + if ( + this.links.some(function(l) { + return l.uid === uid; + }) + ) + uid = uid + "." + this.links.length; return this.links.push(new Link(_source, _target, dir, cls, uid)) - 1; } - linkFor (source, target) { + linkFor(source, target) { for (let i = 0; i < this.links.length; ++i) { - if ((this.links[i].source == source) && (this.links[i].target == target)) + if (this.links[i].source == source && this.links[i].target == target) return this.links[i]; - if ((this.links[i].source == target) && (this.links[i].target == source)) + if (this.links[i].source == target && this.links[i].target == source) return this.links[i]; } // the selected node was a client/broker return null; } - getPosition (name, nodes, source, client, localStorage, height) { - let position = localStorage[name] ? JSON.parse(localStorage[name]) : undefined; - if ((typeof position == 'undefined')) { + getPosition(name, nodes, source, client, localStorage, height) { + let position = localStorage[name] + ? JSON.parse(localStorage[name]) + : undefined; + if (typeof position == "undefined") { position = { - x: Math.round(nodes.get(source).x + 40 * Math.sin(client / (Math.PI * 2.0))), - y: Math.round(nodes.get(source).y + 40 * Math.cos(client / (Math.PI * 2.0))), + x: Math.round( + nodes.get(source).x + 40 * Math.sin(client / (Math.PI * 2.0)) + ), + y: Math.round( + nodes.get(source).y + 40 * Math.cos(client / (Math.PI * 2.0)) + ), fixed: false, animate: true }; - } else - position.animate = false; + } else position.animate = false; if (position.y > height) { - position.y = Math.round(nodes.get(source).y + 40 + Math.cos(client / (Math.PI * 2.0))); + position.y = Math.round( + nodes.get(source).y + 40 + Math.cos(client / (Math.PI * 2.0)) + ); } return position; } - initialize (nodeInfo, nodes, unknowns, localStorage, height) { + initialize(nodeInfo, nodes, unknowns, localStorage, height) { let connectionsPerContainer = {}; let nodeIds = Object.keys(nodeInfo); // collect connection info for each router - for (let source=0; source<nodeIds.length; source++) { + for (let source = 0; source < nodeIds.length; source++) { let onode = nodeInfo[nodeIds[source]]; // skip any routers without connections - if (!onode.connection || !onode.connection.results || onode.connection.results.length === 0) + if ( + !onode.connection || + !onode.connection.results || + onode.connection.results.length === 0 + ) continue; const suid = nodes.get(source).uid(); - for (let c=0; c<onode.connection.results.length; c++) { - let connection = utils.flatten(onode.connection.attributeNames, onode.connection.results[c]); + for (let c = 0; c < onode.connection.results.length; c++) { + let connection = utils.flatten( + onode.connection.attributeNames, + onode.connection.results[c] + ); // this is a connection to another interior router - if (connection.role === 'inter-router') { + if (connection.role === "inter-router") { const target = getContainerIndex(connection.container, nodeInfo); if (target >= 0) { const tuid = nodes.get(target).uid(); - this.getLink(source, target, connection.dir, '', `${suid}-${tuid}`); + this.getLink(source, target, connection.dir, "", `${suid}-${tuid}`); } continue; } if (!connectionsPerContainer[connection.container]) connectionsPerContainer[connection.container] = []; - let linksDir = getLinkDir(connection , onode); - if (linksDir === 'unknown') - unknowns.push(nodeIds[source]); + let linksDir = getLinkDir(connection, onode); + if (linksDir === "unknown") unknowns.push(nodeIds[source]); connectionsPerContainer[connection.container].push({ - source: source, + source: source, linksDir: linksDir, connection: connection, - resultsIndex: c}); + resultsIndex: c + }); } } let unique = {}; // create map of type:id:dir to [containers] for (let container in connectionsPerContainer) { let key = getKey(connectionsPerContainer[container]); - if (!unique[key]) - unique[key] = {c: [], nodes: []}; + if (!unique[key]) unique[key] = { c: [], nodes: [] }; unique[key].c.push(container); } for (let key in unique) { let containers = unique[key].c; - for (let i=0; i<containers.length; i++) { + for (let i = 0; i < containers.length; i++) { let containerId = containers[i]; let connections = connectionsPerContainer[containerId]; let container = connections[0]; - let name = utils.nameFromId(nodeIds[container.source]) + '.' + container.connection.identity; - let position = this.getPosition (name, - nodes, - container.source, - container.resultsIndex, - localStorage, - height); + let name = + utils.nameFromId(nodeIds[container.source]) + + "." + + container.connection.identity; + let position = this.getPosition( + name, + nodes, + container.source, + container.resultsIndex, + localStorage, + height + ); - let node = nodes.getOrCreateNode (nodeIds[container.source], - name, - container.connection.role, - nodes.getLength(), - position.x, position.y, - container.connection.container, - container.resultsIndex, - position.fixed, - container.connection.properties); + let node = nodes.getOrCreateNode( + nodeIds[container.source], + name, + container.connection.role, + nodes.getLength(), + position.x, + position.y, + container.connection.container, + container.resultsIndex, + position.fixed, + container.connection.properties + ); node.host = container.connection.host; node.cdir = container.linksDir; node.user = container.connection.user; node.isEncrypted = container.connection.isEncrypted; node.connectionId = container.connection.identity; - node.uuid = `${node.routerId}${node.nodeType}${node.cdir}`; + node.uuid = `${containerId}-${node.routerId}-${node.nodeType}-${node.cdir}`; // in case a created node (or group) is connected to multiple // routers, we need to remember all the routers for traffic animations - for (let c=1; c<connections.length; c++) { - if (!node.alsoConnectsTo) - node.alsoConnectsTo = []; + for (let c = 1; c < connections.length; c++) { + if (!node.alsoConnectsTo) node.alsoConnectsTo = []; node.alsoConnectsTo.push({ key: nodeIds[connections[c].source], - dir: connections[c].linksDir, - connectionId: connections[c].connection.identity}); + cdir: connections[c].linksDir, + connectionId: connections[c].connection.identity + }); } unique[key].nodes.push(node); } @@ -186,184 +215,80 @@ export class Links { nodes.add(unique[key].nodes[0]); let target = nodes.nodes.length - 1; unique[key].nodes[0].normals = [unique[key].nodes[0]]; - for (let n=1; n<unique[key].nodes.length; n++) { + for (let n = 1; n < unique[key].nodes.length; n++) { unique[key].nodes[0].normals.push(unique[key].nodes[n]); } let containerId = unique[key].c[0]; let links = connectionsPerContainer[containerId]; - for (let l=0; l<links.length; l++) { + for (let l = 0; l < links.length; l++) { let source = links[l].source; const suid = nodes.get(source).uid(); const tuid = nodes.get(target).uid(); - this.getLink(links[l].source, target, links[l].linksDir, 'small', `${suid}-${tuid}`); + this.getLink( + links[l].source, + target, + links[l].linksDir, + "small", + `${suid}-${tuid}` + ); } } } - initializeLinks (nodeInfo, nodes, unknowns, localStorage, height) { - let animate = false; - let client = 1.0; - let nodeIds = Object.keys(nodeInfo); - // loop through all the routers - for (let source=0; source<nodeIds.length; source++) { - let id = nodeIds[source]; - const suid = nodes.get(source).uid(); - let onode = nodeInfo[id]; - if (!onode['connection']) { - continue; - } - let normalsParent = {}; // 1st normal node for this parent - // loop through each connection for this router - for (let j = 0; j < onode['connection'].results.length; j++) { - let connection = utils.flatten(onode['connection'].attributeNames, - onode['connection'].results[j]); - let role = connection.role; - let dir = connection.dir; - // connection to another interior router, just create a link between them - if (role == 'inter-router') { - let connId = connection.container; - let target = getContainerIndex(connId, nodeInfo); - if (target >= 0) { - const tuid = nodes.get(target).uid(); - this.getLink(source, target, dir, '', suid + '-' + tuid); - } - continue; - } - let properties = connection.properties || {}; - // handle external connections - let name = utils.nameFromId(id) + '.' + connection.identity; - // if we have any new clients, animate the force graph to position them - let position = localStorage[name] ? JSON.parse(localStorage[name]) : undefined; - if ((typeof position == 'undefined')) { - animate = true; - position = { - x: Math.round(nodes.get(source).x + 40 * Math.sin(client / (Math.PI * 2.0))), - y: Math.round(nodes.get(source).y + 40 * Math.cos(client / (Math.PI * 2.0))), - fixed: false - }; - } - if (position.y > height) { - position.y = Math.round(nodes.get(source).y + 40 + Math.cos(client / (Math.PI * 2.0))); - } - let existingNodeIndex = nodes.nodeExists(connection.container); - let normalInfo = nodes.normalExists(connection.container); - let node = nodes.getOrCreateNode(id, name, role, nodeInfo, nodes.getLength(), position.x, position.y, connection.container, j, position.fixed, properties); - let nodeType = utils.isAConsole(properties, connection.identity, role, node.key) ? 'console' : 'client'; - let linksDir = getLinkDir(connection, onode); - if (existingNodeIndex >= 0) { - // make a link between the current router (source) and the existing node - const tuid = nodes.get(existingNodeIndex).uid(); - this.getLink(source, existingNodeIndex, dir, 'small', suid + '-' + tuid); - } else if (normalInfo.nodesIndex) { - // get node index of node that contained this connection in its normals array - let normalSource = this.getLinkSource(normalInfo.nodesIndex); - if (normalSource >= 0) { - if (linksDir === 'unknown') - linksDir = dir; - node.cdir = linksDir; - nodes.add(node); - const suidn = nodes.get(this.links[normalSource].source).uid(); - const tuid = node.uid(); - // create link from original node to the new node - this.getLink(this.links[normalSource].source, nodes.getLength()-1, linksDir, 'small', suidn + '-' + tuid); - // create link from this router to the new node - this.getLink(source, nodes.getLength()-1, linksDir, 'small', suid + '-' + tuid); - // remove the old node from the normals list - nodes.get(normalInfo.nodesIndex).normals.splice(normalInfo.normalsIndex, 1); - } - } else if (role === 'normal' || role === 'edge') { - // normal nodes can be collapsed into a single node if they are all the same dir - if (linksDir !== 'unknown') { - node.user = connection.user; - node.isEncrypted = connection.isEncrypted; - node.host = connection.host; - node.connectionId = connection.identity; - node.cdir = linksDir; - node.uuid = `${node.routerId}${node.nodeType}${node.cdir}`; - // determine arrow direction by using the link directions - if (!normalsParent[nodeType+linksDir]) { - normalsParent[nodeType+linksDir] = node; - nodes.add(node); - node.normals = [node]; - node.connectsTo = {id: linksDir}; - // now add a link - this.getLink(source, nodes.getLength() - 1, linksDir, 'small', suid + '-' + node.uid()); - client++; - } else { - normalsParent[nodeType+linksDir].normals.push(node); - } - } else { - node.id = nodes.getLength() - 1 + unknowns.length; - unknowns.push(node); - } - } else { - nodes.add(node); - // now add a link - this.getLink(source, nodes.getLength() - 1, dir, 'small', suid + '-' + node.uid()); - client++; - } - } - } - return animate; - } - clearHighlighted () { + clearHighlighted() { for (let i = 0; i < this.links.length; ++i) { this.links[i].highlighted = false; } } } -var getContainerIndex = function (_id, nodeInfo) { +var getContainerIndex = function(_id, nodeInfo) { let nodeIndex = 0; for (let id in nodeInfo) { - if (utils.nameFromId(id) === _id) - return nodeIndex; + if (utils.nameFromId(id) === _id) return nodeIndex; ++nodeIndex; } return -1; }; -var getLinkDir = function (connection, onode) { - let links = onode['router.link']; +var getLinkDir = function(connection, onode) { + let links = onode["router.link"]; if (!links) { - return 'unknown'; + return "unknown"; } - let inCount = 0, outCount = 0; - let typeIndex = links.attributeNames.indexOf('linkType'); - let connectionIdIndex = links.attributeNames.indexOf('connectionId'); - let dirIndex = links.attributeNames.indexOf('linkDir'); - links.results.forEach( function (linkResult) { - if (linkResult[typeIndex] === 'endpoint' && linkResult[connectionIdIndex] === connection.identity) - if (linkResult[dirIndex] === 'in') - ++inCount; - else - ++outCount; + let inCount = 0, + outCount = 0; + let typeIndex = links.attributeNames.indexOf("linkType"); + let connectionIdIndex = links.attributeNames.indexOf("connectionId"); + let dirIndex = links.attributeNames.indexOf("linkDir"); + links.results.forEach(function(linkResult) { + if ( + linkResult[typeIndex] === "endpoint" && + linkResult[connectionIdIndex] === connection.identity + ) + if (linkResult[dirIndex] === "in") ++inCount; + else ++outCount; }); - if (inCount > 0 && outCount > 0) - return 'both'; - if (inCount > 0) - return 'in'; - if (outCount > 0) - return 'out'; - return 'unknown'; + if (inCount > 0 && outCount > 0) return "both"; + if (inCount > 0) return "in"; + if (outCount > 0) return "out"; + return "unknown"; }; -var getKey = function (containers) { +var getKey = function(containers) { let parts = []; let connection = containers[0].connection; - let d = {nodeType: connection.role, properties: connection.properties || {}}; - let connectionType = 'client'; - if (utils.isConsole(connection)) - connectionType = 'console'; - else if (utils.isArtemis(d)) - connectionType = 'artemis'; - else if (utils.isQpid(d)) - connectionType = 'qpid'; - else if (connection.role === 'edge') - connectionType = 'edge'; - for (let c=0; c<containers.length; c++) { + let d = { + nodeType: connection.role, + properties: connection.properties || {} + }; + let connectionType = "client"; + if (utils.isConsole(connection)) connectionType = "console"; + else if (utils.isArtemis(d)) connectionType = "artemis"; + else if (utils.isQpid(d)) connectionType = "qpid"; + else if (connection.role === "edge") connectionType = "edge"; + for (let c = 0; c < containers.length; c++) { let container = containers[c]; parts.push(`${container.source}-${container.linksDir}`); } - return `${connectionType}:${parts.join(':')}`; + return `${connectionType}:${parts.join(":")}`; }; - http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/55b7ae55/console/stand-alone/plugin/js/topology/map.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/js/topology/map.js b/console/stand-alone/plugin/js/topology/map.js index c14008d..972d894 100644 --- a/console/stand-alone/plugin/js/topology/map.js +++ b/console/stand-alone/plugin/js/topology/map.js @@ -57,6 +57,10 @@ export class BackgroundMap { // eslint-disable-line no-unused-vars init($scope, svg, width, height) { return new Promise( (function (resolve, reject) { + if (this.initialized) { + resolve(); + return; + } this.svg = svg; this.width = width; this.height = height; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
