http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/js/qdrOverview.js ---------------------------------------------------------------------- diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrOverview.js b/console/hawtio/src/main/webapp/plugin/js/qdrOverview.js new file mode 100644 index 0000000..cc52868 --- /dev/null +++ b/console/hawtio/src/main/webapp/plugin/js/qdrOverview.js @@ -0,0 +1,688 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ +/** + * @module QDR + */ +/** + * @module QDR + */ +var QDR = (function (QDR) { + + /** + * @method OverviewController + * @param $scope + * @param QDRService + * @param QDRChartServer + * dialogServer + * $location + * + * Controller that handles the QDR overview page + */ + QDR.module.controller("QDR.OverviewController", ['$scope', 'QDRService', '$location', '$timeout', function($scope, QDRService, $location, $timeout) { + + + console.log("QDR.OverviewControll started with location of " + $location.path() + " and connection of " + QDRService.connected); + + if (!QDRService.connected) { + // we are not connected. we probably got here from a bookmark or manual page reload + $location.path("/dispatch_plugin/connect") + return; + } + + var nodeIds = QDRService.nodeIdList(); + var currentTimer; + var refreshInterval = 5000 + $scope.modes = [ + {title: 'Overview', name: 'Overview', right: false} + ]; + + $scope.templates = + [ { name: 'Routers', url: 'routers.html'}, + { name: 'Router', url: 'router.html'}, + { name: 'Addresses', url: 'addresses.html'}, + { name: 'Address', url: 'address.html'}, + { name: 'Connections', url: 'connections.html'}, + { name: 'Connection', url: 'connection.html'}, + { name: 'Logs', url: 'logs.html'}, + { name: 'Log', url: 'log.html'} + ]; + var topLevelChildren = []; + + $scope.allRouterFields = []; + var allRouterCols = [ + { + field: 'routerId', + displayName: 'Router' + }, + { + field: 'area', + displayName: 'Area' + }, + { + field: 'mode', + displayName: 'Mode' + }, + { + field: 'connections', + displayName: 'External connections' + }, + { + field: 'addrCount', + displayName: 'Address count' + }, + { + field: 'linkCount', + displayName: 'Link count' + } + ]; + + //cellTemplate: '<div class="ngCellText"><a ng-click="openMessageDialog(row)">{{row.entity.JMSMessageID}}</a></div>', + + $scope.allRouterSelected = function (row ) { + console.log("row selected" + row) + } + function afterSelectionChange(rowItem, checkAll) { + var nodeId = rowItem.entity.nodeId; + $("#overtree").dynatree("getTree").activateKey(nodeId); + } + + $scope.allRouterSelections = []; + $scope.allRouters = { + data: 'allRouterFields', + columnDefs: allRouterCols, + enableColumnResize: true, + multiSelect: false, + selectedItems: $scope.allRouterSelections, + afterSelectionChange: function(data) { + if (data.selected) { + var selItem = $scope.allRouterSelections[0] + var nodeId = selItem.nodeId + // activate Routers->nodeId in the tree + $("#overtree").dynatree("getTree").activateKey(nodeId); + + } + } + }; + + // get info for all routers + var allRouterInfo = function () { + nodeIds = QDRService.nodeIdList() + var expected = Object.keys(nodeIds).length + var received = 0; + var allRouterFields = []; + var gotNodeInfo = function (nodeName, entity, response) { + var results = response.results; + var name = QDRService.nameFromId(nodeName) + var connections = 0; + results.forEach( function (result) { + var role = QDRService.valFor(response.attributeNames, result, "role") + if (role != 'inter-router') { + ++connections + } + }) + allRouterFields.push({routerId: name, connections: connections, nodeId: nodeName}) + ++received + if (expected == received) { + allRouterFields.sort ( function (a,b) { return a.routerId < b.routerId ? -1 : a.routerId > b.routerId ? 1 : 0}) + // now get each router's node info + QDRService.getMultipleNodeInfo(nodeIds, "router", [], function (nodeIds, entity, response) { + var results = response.aggregates + results.forEach ( function (result) { + + var routerId = QDRService.valFor(response.attributeNames, result, "routerId").sum + allRouterFields.some( function (connField) { + if (routerId === connField.routerId) { + response.attributeNames.forEach ( function (attrName) { + connField[attrName] = QDRService.valFor(response.attributeNames, result, attrName).sum + }) + return true + } + return false + }) + }) + $scope.allRouterFields = allRouterFields + $scope.$apply() + if (currentTimer) { + clearTimeout(currentTimer) + } + currentTimer = setTimeout(allRouterInfo, refreshInterval); + }, nodeIds[0]) + } + } + nodeIds.forEach ( function (nodeId) { + QDRService.getNodeInfo(nodeId, ".connection", ["role"], gotNodeInfo) + }) + + } + + // get info for a single router + var routerInfo = function (node) { + $scope.router = node + $scope.routerFields = [] + var cols = [ + { + field: 'attribute', + displayName: 'Attribute', + width: '40%' + }, + { + field: 'value', + displayName: 'Value', + width: '40%' + } + ] + $scope.routerGrid = { + data: 'routerFields', + columnDefs: cols, + enableColumnResize: true, + multiSelect: false + } + + $scope.allRouterFields.some( function (field) { + if (field.routerId === node.data.title) { + Object.keys(field).forEach ( function (key) { + if (key !== '$$hashKey') + $scope.routerFields.push({attribute: key, value: field[key]}) + }) + return true + } + }) + if (currentTimer) { + clearTimeout(currentTimer) + currentTimer = null + } + } + + // get info for a all addresses + var allAddressInfo = function () { + $scope.addressFields = [] + var addressCols = [ + { + field: 'address', + displayName: 'address' + }, + { + field: 'class', + displayName: 'class' + }, + { + field: 'phase', + displayName: 'phase', + cellClass: 'grid-align-value' + }, + { + field: 'inproc', + displayName: 'in-proc' + }, + { + field: 'local', + displayName: 'local', + cellClass: 'grid-align-value' + }, + { + field: 'remote', + displayName: 'remote', + cellClass: 'grid-align-value' + }, + { + field: 'in', + displayName: 'in', + cellClass: 'grid-align-value' + }, + { + field: 'out', + displayName: 'out', + cellClass: 'grid-align-value' + } + ] + $scope.selectedAddresses = [] + $scope.addressGrid = { + data: 'addressFields', + columnDefs: addressCols, + enableColumnResize: true, + multiSelect: false, + selectedItems: $scope.selectedAddresses, + afterSelectionChange: function(data) { + if (data.selected) { + var selItem = data.entity; + var nodeId = selItem.uid + $("#overtree").dynatree("getTree").activateKey(nodeId); + + } + } + }; + var gotAllAddressFields = function ( addressFields ) { + $scope.addressFields = addressFields + $scope.$apply() + if (currentTimer) { + clearTimeout(currentTimer) + } + currentTimer = setTimeout(allAddressInfo, refreshInterval); + } + getAllAddressFields(gotAllAddressFields) + } + + var getAllAddressFields = function (callback) { + var addr_class = function (addr) { + if (!addr) return "-" + if (addr[0] == 'M') return "mobile" + if (addr[0] == 'R') return "router" + if (addr[0] == 'A') return "area" + if (addr[0] == 'L') return "local" + if (addr[0] == 'C') return "link-incoming" + if (addr[0] == 'D') return "link-outgoing" + return "unknown: " + addr[0] + } + + var addr_text = function (addr) { + if (!addr) + return "-" + if (addr[0] == 'M') + return addr.substring(2) + else + return addr.substring(1) + } + + var addr_phase = function (addr) { + if (!addr) + return "-" + if (addr[0] == 'M') + return addr[1] + return '' + } + + var identity_clean = function (identity) { + if (!identity) + return "-" + var pos = identity.indexOf('/') + if (pos >= 0) + return identity.substring(pos + 1) + return identity + } + + var addressFields = [] + nodeIds = QDRService.nodeIdList() + QDRService.getMultipleNodeInfo(nodeIds, "router.address", [], function (nodeIds, entity, response) { + response.aggregates.forEach( function (result) { + var prettySum = function (field) { + var fieldIndex = response.attributeNames.indexOf(field) + if (fieldIndex < 0) { + return "-" + } + var val = result[fieldIndex].sum + return QDRService.pretty(val) + } + + var uid = QDRService.valFor(response.attributeNames, result, "identity").sum + var identity = identity_clean(uid) + + addressFields.push({ + address: addr_text(identity), + 'class': addr_class(identity), + phase: addr_phase(identity), + inproc: prettySum("inProcess"), + local: prettySum("subscriberCount"), + remote: prettySum("remoteCount"), + 'in': prettySum("deliveriesIngress"), + out: prettySum("deliveriesEgress"), + thru: prettySum("deliveriesTransit"), + toproc: prettySum("deliveriesToContainer"), + fromproc:prettySum("deliveriesFromContainer"), + uid: uid + }) + }) + callback(addressFields) + }, nodeIds[0]) + } + + + // get info for a all connections + var allConnectionInfo = function () { + $scope.allConnectionFields = [] + var allConnectionCols = [ + { + field: 'host', + displayName: 'host' + }, + { + field: 'container', + displayName: 'container' + }, + { + field: 'role', + displayName: 'role' + }, + { + field: 'dir', + displayName: 'dir' + }, + { + field: 'security', + displayName: 'security' + }, + { + field: 'authentication', + displayName: 'authentication' + } + ] + $scope.allConnectionSelections = []; + $scope.allConnectionGrid = { + data: 'allConnectionFields', + columnDefs: allConnectionCols, + enableColumnResize: true, + multiSelect: false, + selectedItems: $scope.allConnectionSelections, + afterSelectionChange: function(data) { + if (data.selected) { + var selItem = $scope.allConnectionSelections[0] + var nodeId = selItem.host + // activate Routers->nodeId in the tree + $("#overtree").dynatree("getTree").activateKey(nodeId); + + } + } + }; +/* + $scope.allConnectionGrid = { + data: 'allConnectionFields', + columnDefs: allConnectionCols, + enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER, + enableVerticalScrollbar: uiGridConstants.scrollbars.NEVER, + enableColumnResize: true, + multiSelect: false, + enableRowHeaderSelection: false, + noUnselect: true, + enableSelectAll: false, + enableRowSelection: true, + onRegisterApi: function (gridApi) { + gridApi.selection.on.rowSelectionChanged($scope, function(row) { + if (row.isSelected) { + var host = row.entity.host; + $("#overtree").fancytree("getTree").activateKey(host); + } + }); + } + } +*/ + connections.children.forEach( function (connection) { + $scope.allConnectionFields.push(connection.fields) + }) + if (currentTimer) { + clearTimeout(currentTimer) + currentTimer = null + } + } + + // get info for a single address + var addressInfo = function (address) { + $scope.address = address + $scope.addressFields = [] + var cols = [ + { + field: 'attribute', + displayName: 'Attribute', + width: '40%' + }, + { + field: 'value', + displayName: 'Value', + width: '40%' + } + ] + $scope.addressGrid = { + data: 'addressFields', + columnDefs: cols, + enableColumnResize: true, + multiSelect: false + } + + var fields = Object.keys(address.data.fields) + fields.forEach( function (field) { + if (field != "title" && field != "uid") + $scope.addressFields.push({attribute: field, value: address.data.fields[field]}) + }) + + if (currentTimer) { + clearTimeout(currentTimer) + currentTimer = null + } + } + + // get info for a single connection + var connectionInfo = function (connection) { + $scope.connection = connection + $scope.connectionFields = [] + var cols = [ + { + field: 'attribute', + displayName: 'Attribute', + width: '40%' + }, + { + field: 'value', + displayName: 'Value', + width: '40%' + } + ] + $scope.connectionGrid = { + data: 'connectionFields', + columnDefs: cols, + enableColumnResize: true, + multiSelect: false + } + + var fields = Object.keys(connection.data.fields) + fields.forEach( function (field) { + $scope.connectionFields.push({attribute: field, value: connection.data.fields[field]}) + }) + if (currentTimer) { + clearTimeout(currentTimer) + currentTimer = null + } + } + + // get info for a all logs + var allLogInfo = function () { + } + + // get info for a single log + var logInfo = function (node) { + $scope.log = node + } + + var activated = function (node) { + //QDR.log.debug("node activated: " + node.data.title) + var type = node.data.type; + var template = $scope.templates.filter( function (tpl) { + return tpl.name == type; + }) + $scope.template = template[0]; + if (node.data.info) { + node.data.info(node) + if (!$scope.$$phase) $scope.$apply() + } + + } + $scope.template = $scope.templates[0]; + + /* -------------------------------------------------- + * + * setup the tree on the left + * + * ------------------------------------------------- + */ + var routers = new Folder("Routers") + routers.type = "Routers" + routers.info = allRouterInfo + routers.focus = true + routers.expanded = true + routers.key = "Routers" + routers.addClass = "routers" + topLevelChildren.push(routers) + nodeIds.forEach( function (node) { + var name = QDRService.nameFromId(node) + var router = new Folder(name) + router.type = "Router" + router.info = routerInfo + router.nodeId = node + router.key = node + router.addClass = "router" + routers.children.push(router) + }) + + var expected = nodeIds.length; + var addresses = new Folder("Addresses") + addresses.type = "Addresses" + addresses.info = allAddressInfo + addresses.key = "Addresses" + addresses.addClass = "addresses" + topLevelChildren.push(addresses) + + var gotAddressFields = function (addressFields) { + addressFields.sort ( function (a,b) { return a.address < b.address ? -1 : a.address > b.address ? 1 : 0}) + addressFields[0].title = addressFields[0].address + for (var i=1; i<addressFields.length; ++i) { + if (addressFields[i].address === addressFields[i-1].address) { + addressFields[i-1].title = addressFields[i-1].address + " (" + addressFields[i-1]['class'] + ")" + addressFields[i].title = addressFields[i].address + " (" + addressFields[i]['class'] + ")" + } else + addressFields[i].title = addressFields[i].address + } + addressFields.forEach( function (address) { + var a = new Folder(address.title) + a.info = addressInfo + a.key = address.uid + a.fields = address + a.type = "Address" + a.addClass = "address" + addresses.children.push(a) + } ) + } + getAllAddressFields(gotAddressFields) + + + var connreceived = 0; + var connectionsObj = {} + var connections = new Folder("Connections") + connections.type = "Connections" + connections.info = allConnectionInfo + connections.key = "Connections" + connections.addClass = "connections" + topLevelChildren.push(connections) + nodeIds.forEach( function (nodeId) { + + QDRService.getNodeInfo(nodeId, ".connection", [], function (nodeName, entity, response) { + response.results.forEach( function (result) { + + var auth = "no_auth" + var sasl = QDRService.valFor(response.attributeNames, result, "sasl") + if (QDRService.valFor(response.attributeNames, result, "isAuthenticated")) { + auth = sasl + if (sasl === "ANONYMOUS") + auth = "anonymous-user" + else { + if (sasl === "GSSAPI") + sasl = "Kerberos" + if (sasl === "EXTERNAL") + sasl = "x.509" + auth = QDRService.valFor(response.attributeNames, result, "user") + "(" + + QDRService.valFor(response.attributeNames, result, "sslCipher") + ")" + } + } + + var sec = "no-security" + if (QDRService.valFor(response.attributeNames, result, "isEncrypted")) { + if (sasl === "GSSAPI") + sec = "Kerberos" + else + sec = QDRService.valFor(response.attributeNames, result, "sslProto") + "(" + + QDRService.valFor(response.attributeNames, result, "sslCipher") + ")" + } + + var host = QDRService.valFor(response.attributeNames, result, "host") + connectionsObj[host] = {} + response.attributeNames.forEach( function (attribute, i) { + connectionsObj[host][attribute] = result[i] + }) + connectionsObj[host].security = sec + connectionsObj[host].authentication = auth + }) + ++connreceived; + if (connreceived == expected) { + var allConnections = Object.keys(connectionsObj).sort() + allConnections.forEach(function (connection) { + var c = new Folder(connection) + c.type = "Connection" + c.info = connectionInfo + c.key = connection + c.fields = connectionsObj[connection] + c.tooltip = connectionsObj[connection].role === "inter-router" ? "inter-router connection" : "external connection" + c.addClass = c.tooltip + connections.children.push(c) + }) + } + }) + }) + + var logsreceived = 0; + var logObj = {} + var logs = new Folder("Logs") + logs.type = "Logs" + logs.info = allLogInfo + logs.key = "Logs" + //topLevelChildren.push(logs) + nodeIds.forEach( function (nodeId) { + QDRService.getNodeInfo(nodeId, ".log", ["name"], function (nodeName, entity, response) { + response.results.forEach( function (result) { + logObj[result[0]] = 1 // use object to collapse duplicates + }) + ++logsreceived; + if (logsreceived == expected) { + var allLogs = Object.keys(logObj).sort() + allLogs.forEach(function (log) { + var l = new Folder(log) + l.type = "Log" + l.info = logInfo + l.key = log + logs.children.push(l) + }) + $('#overtree').dynatree({ + onActivate: activated, + selectMode: 1, + activeVisible: false, + children: topLevelChildren + }) + allRouterInfo(); + } + }) + }) + + $scope.$on("$destroy", function( event ) { + if (currentTimer) { + clearTimeout(currentTimer) + currentTimer = null; + } + }); + + }]); + + return QDR; + +}(QDR || {})); +
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/js/qdrSchema.js ---------------------------------------------------------------------- diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrSchema.js b/console/hawtio/src/main/webapp/plugin/js/qdrSchema.js new file mode 100644 index 0000000..dc39f9c --- /dev/null +++ b/console/hawtio/src/main/webapp/plugin/js/qdrSchema.js @@ -0,0 +1,38 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ +/** + * @module QDR + */ +var QDR = (function (QDR) { + + QDR.module.controller("QDR.SchemaController", ['$scope', '$location', 'QDRService', function($scope, $location, QDRService) { + if (!QDRService.connected) { + // we are not connected. we probably got here from a bookmark or manual page reload + $location.path("/dispatch_plugin/connect") + $location.search('org', "schema"); + + return; + } + + $scope.schema = QDRService.schema; + + }]); + + return QDR; +}(QDR || {})); http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/js/qdrService.js ---------------------------------------------------------------------- diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrService.js b/console/hawtio/src/main/webapp/plugin/js/qdrService.js new file mode 100644 index 0000000..06bf113 --- /dev/null +++ b/console/hawtio/src/main/webapp/plugin/js/qdrService.js @@ -0,0 +1,731 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +/** + * @module QDR + */ +var QDR = (function(QDR) { + + // The QDR service handles the connection to + // the server in the background + QDR.module.factory("QDRService", ['$rootScope', '$http', '$resource', '$location', function($rootScope, $http, $resource, $location) { + var self = { + + rhea: require("rhea"), + + timeout: 10, + connectActions: [], + disconnectActions: [], + updatedActions: {}, + stop: undefined, // update interval handle + + addConnectAction: function(action) { + if (angular.isFunction(action)) { + self.connectActions.push(action); + } + }, + addDisconnectAction: function(action) { + if (angular.isFunction(action)) { + self.disconnectActions.push(action); + } + }, + addUpdatedAction: function(key, action) { + if (angular.isFunction(action)) { + self.updatedActions[key] = action; + } + }, + delUpdatedAction: function(key) { + if (key in self.updatedActions) + delete self.updatedActions[key]; + }, + + executeConnectActions: function() { + self.connectActions.forEach(function(action) { + //QDR.log.debug("executing connect action " + action); + action.apply(); + }); + self.connectActions = []; + }, + executeDisconnectActions: function() { + self.disconnectActions.forEach(function(action) { + action.apply(); + }); + self.disconnectActions = []; + }, + executeUpdatedActions: function() { + for (action in self.updatedActions) { + self.updatedActions[action].apply(); + } + }, + + notifyTopologyDone: function() { + //QDR.log.debug("got Toplogy done notice"); + + if (!angular.isDefined(self.schema)) + return; + else if (self.topology._gettingTopo) + return; + if (!self.gotTopology) { + QDR.log.debug("topology was just initialized"); + self.gotTopology = true; + self.executeConnectActions(); + $rootScope.$apply(); + } else { + QDR.log.debug("topology model was just updated"); + self.executeUpdatedActions(); + } + + }, + /** + * @property options + * Holds a reference to the connection options when + * a connection is started + */ + options: undefined, + + /* + * @property message + * The proton message that is used to send commands + * and receive responses + */ + sender: undefined, + receiver: undefined, + sendable: false, + + schema: undefined, + + toAddress: undefined, + connected: false, + gotTopology: false, + errorText: undefined, + connectionError: undefined, + + isConnected: function() { + return self.connected; + }, + + correlator: { + _objects: {}, + _corremationID: 0, + + corr: function () { + var id = ++this._corremationID + ""; + this._objects[id] = {resolver: null} + return id; + }, + request: function() { + //QDR.log.debug("correlator:request"); + return this; + }, + then: function(id, resolver, error) { + //QDR.log.debug("registered then resolver for correlationID: " + id); + if (error) { + delete this._objects[id]; + return; + } + this._objects[id].resolver = resolver; + }, + // called by receiver's on('message') handler when a response arrives + resolve: function(context) { + var correlationID = context.message.properties.correlation_id; + this._objects[correlationID].resolver(context.message.body); + delete this._objects[correlationID]; + } + }, + + onSubscription: function() { + self.getSchema(); + self.topology.get(); + }, + + startUpdating: function () { + QDR.log.info("startUpdating called") + self.stopUpdating(); + self.topology.get(); + self.stop = setInterval(function() { + self.topology.get(); + }, 2000); + }, + stopUpdating: function () { + if (angular.isDefined(self.stop)) { + QDR.log.info("stopUpdating called") + clearInterval(self.stop); + self.stop = undefined; + } + }, + + initProton: function() { + //QDR.log.debug("*************QDR init proton called ************"); + }, + cleanUp: function() { + }, + error: function(line) { + if (line.num) { + QDR.log.debug("error - num: ", line.num, " message: ", line.message); + } else { + QDR.log.debug("error - message: ", line.message); + } + }, + disconnected: function(line) { + QDR.log.debug("Disconnected from QDR server"); + self.executeDisconnectActions(); + }, + + nameFromId: function (id) { + return id.split('/')[3]; + }, + + humanify: function (s) { + var t = s.charAt(0).toUpperCase() + s.substr(1).replace(/[A-Z]/g, ' $&'); + return t.replace(".", " "); + }, + pretty: function(v) { + var formatComma = d3.format(","); + if (!isNaN(parseFloat(v)) && isFinite(v)) + return formatComma(v); + return v; + }, + + nodeNameList: function() { + var nl = []; + // if we are in the middel of updating the topology + // then use the last known node info + var ni = self.topology._nodeInfo; + if (self.topology._gettingTopo) + ni = self.topology._lastNodeInfo; + for (var id in ni) { + nl.push(self.nameFromId(id)); + } + return nl.sort(); + }, + + nodeIdList: function() { + var nl = []; + // if we are in the middel of updating the topology + // then use the last known node info + var ni = self.topology._nodeInfo; + if (self.topology._gettingTopo) + ni = self.topology._lastNodeInfo; + for (var id in ni) { + nl.push(id); + } + return nl.sort(); + }, + + nodeList: function () { + var nl = []; + var ni = self.topology._nodeInfo; + if (self.topology._gettingTopo) + ni = self.topology._lastNodeInfo; + for (var id in ni) { + nl.push({name: self.nameFromId(id), id: id}); + } + return nl; + }, + + // given an attribute name array, find the value at the same index in the values array + valFor: function (aAr, vAr, key) { + var idx = aAr.indexOf(key); + if ((idx > -1) && (idx < vAr.length)) { + return vAr[idx]; + } + return null; + }, + + /* + * send the management messages that build up the topology + * + * + */ + topology: { + _gettingTopo: false, + _nodeInfo: {}, + _lastNodeInfo: {}, + _expected: {}, + _timerHandle: null, + + nodeInfo: function () { + return this._gettingTopo ? this._lastNodeInfo : this._nodeInfo; + }, + + get: function () { + if (this._gettingTopo) + return; + if (!self.connected) { + QDR.log.debug("topology get failed because !self.connected") + return; + } + this._lastNodeInfo = angular.copy(this._nodeInfo); + this._gettingTopo = true; + + self.errorText = undefined; + this.cleanUp(this._nodeInfo); + this._nodeInfo = {}; + this._expected = {}; + + // get the list of nodes to query. + // once this completes, we will get the info for each node returned + self.getRemoteNodeInfo( function (response) { + //QDR.log.debug("got remote node list of "); + //console.dump(response); + if( Object.prototype.toString.call( response ) === '[object Array]' ) { + // we expect a response for each of these nodes + self.topology.wait(self.timeout); + for (var i=0; i<response.length; ++i) { + self.makeMgmtCalls(response[i]); + } + }; + }); + }, + + cleanUp: function (obj) { +/* + for (var o in obj) { + QDR.log.debug("cleaning up"); + console.dump(obj[o]); + if (isNaN(parseInt(o))) + this.cleanUp(obj[o]); + } +*/ + if (obj) + delete obj; + }, + wait: function (timeout) { + this.timerHandle = setTimeout(this.timedOut, timeout * 1000); + }, + timedOut: function () { + // a node dropped out. this happens when the get-mgmt-nodex + // results contains more nodes than actually respond within + // the timeout. However, if the responses we get don't contain + // the missing node, assume we are done. + QDR.log.debug("timed out waiting for management responses"); + // note: can't use 'this' in a timeout handler + self.topology.dump("state at timeout"); + // check if _nodeInfo is consistent + if (self.topology.isConsistent()) { + //TODO: notify controllers which node was dropped + // so they can keep an event log + self.topology.ondone(); + return; + } + self.topology.onerror(Error("Timed out waiting for management responses")); + }, + isConsistent: function () { + // see if the responses we have so far reference any nodes + // for which we don't have a response + var gotKeys = {}; + for (var id in this._nodeInfo) { + var onode = this._nodeInfo[id]; + var conn = onode['.connection']; + // get list of node names in the connection data + if (conn) { + var containerIndex = conn.attributeNames.indexOf('container'); + var connectionResults = conn.results; + if (containerIndex >= 0) + for (var j=0; j < connectionResults.length; ++j) { + // inter-router connection to a valid dispatch connection name + gotKeys[connectionResults[j][containerIndex]] = ""; // just add the key + } + } + } + // gotKeys now contains all the container names that we have received + // Are any of the keys that are still expected in the gotKeys list? + var keys = Object.keys(gotKeys); + for (var id in this._expected) { + var key = self.nameFromId(id); + if (key in keys) + return false; + } + return true; + }, + + addNodeInfo: function (id, entity, values) { + // save the results in the nodeInfo object + if (id) { + if (!(id in self.topology._nodeInfo)) { + self.topology._nodeInfo[id] = {}; + } + self.topology._nodeInfo[id][entity] = values; + } + + // remove the id / entity from _expected + if (id in self.topology._expected) { + var entities = self.topology._expected[id]; + var idx = entities.indexOf(entity); + if (idx > -1) { + entities.splice(idx, 1); + if (entities.length == 0) + delete self.topology._expected[id]; + } + } + // see if the expected obj is empty + if (Object.getOwnPropertyNames(self.topology._expected).length == 0) + self.topology.ondone(); + self.topology.cleanUp(values); + }, + expect: function (id, key) { + if (!key || !id) + return; + if (!(id in this._expected)) + this._expected[id] = []; + if (this._expected[id].indexOf(key) == -1) + this._expected[id].push(key); + }, +/* +The response looks like: +{ + ".router": { + "results": [ + [4, "router/QDR.X", 1, "0", 3, 60, 60, 11, "QDR.X", 30, "interior", "org.apache.qpid.dispatch.router", 5, 12, "router/QDR.X"] + ], + "attributeNames": ["raIntervalFlux", "name", "helloInterval", "area", "helloMaxAge", "mobileAddrMaxAge", "remoteLsMaxAge", "addrCount", "routerId", "raInterval", "mode", "type", "nodeCount", "linkCount", "identity"] + }, + ".connection": { + "results": [ + ["QDR.B", "connection/0.0.0.0:20002", "operational", "0.0.0.0:20002", "inter-router", "connection/0.0.0.0:20002", "ANONYMOUS", "org.apache.qpid.dispatch.connection", "out"], + ["QDR.A", "connection/0.0.0.0:20001", "operational", "0.0.0.0:20001", "inter-router", "connection/0.0.0.0:20001", "ANONYMOUS", "org.apache.qpid.dispatch.connection", "out"], + ["b2de2f8c-ef4a-4415-9a23-000c2f86e85d", "connection/localhost:33669", "operational", "localhost:33669", "normal", "connection/localhost:33669", "ANONYMOUS", "org.apache.qpid.dispatch.connection", "in"] + ], + "attributeNames": ["container", "name", "state", "host", "role", "identity", "sasl", "type", "dir"] + }, + ".router.node": { + "results": [ + ["QDR.A", null], + ["QDR.B", null], + ["QDR.C", "QDR.A"], + ["QDR.D", "QDR.A"], + ["QDR.Y", "QDR.A"] + ], + "attributeNames": ["routerId", "nextHop"] + } +}*/ + ondone: function () { + clearTimeout(this.timerHandle); + this._gettingTopo = false; + //this.miniDump(); + //this.dump(); + self.notifyTopologyDone(); + + }, + dump: function (prefix) { + if (prefix) + QDR.log.debug(prefix); + QDR.log.debug("---"); + for (var key in this._nodeInfo) { + QDR.log.debug(key); + console.dump(this._nodeInfo[key]); + QDR.log.debug("---"); + } + QDR.log.debug("was still expecting:"); + console.dump(this._expected); + }, + miniDump: function (prefix) { + if (prefix) + QDR.log.debug(prefix); + QDR.log.debug("---"); + console.dump(Object.keys(this._nodeInfo)); + QDR.log.debug("---"); + }, + onerror: function (err) { + this._gettingTopo = false; + QDR.log.debug("Err:" + err); + self.executeDisconnectActions(); + + } + + }, + + getRemoteNodeInfo: function (callback) { + //QDR.log.debug("getRemoteNodeInfo called"); + var ret; + // first get the list of remote node names + self.correlator.request( + ret = self.sendMgmtQuery('GET-MGMT-NODES') + ).then(ret.id, function(response) { + callback(response); + self.topology.cleanUp(response); + }, ret.error); + }, + + makeMgmtCalls: function (id) { + var keys = [".router", ".connection", ".container", ".router.node", ".listener", ".router.link"]; + $.each(keys, function (i, key) { + self.topology.expect(id, key); + self.getNodeInfo(id, key, [], self.topology.addNodeInfo); + }); + }, + + getNodeInfo: function (nodeName, entity, attrs, callback) { + //QDR.log.debug("getNodeInfo called with nodeName: " + nodeName + " and entity " + entity); + var ret; + self.correlator.request( + ret = self.sendQuery(nodeName, entity, attrs) + ).then(ret.id, function(response) { + callback(nodeName, entity, response); + //self.topology.addNodeInfo(nodeName, entity, response); + //self.topology.cleanUp(response); + }, ret.error); + }, + + getMultipleNodeInfo: function (nodeNames, entity, attrs, callback, selectedNodeId) { + var responses = {}; + var gotNodesResult = function (nodeName, dotentity, response) { + responses[nodeName] = response; + if (Object.keys(responses).length == nodeNames.length) { + aggregateNodeInfo(nodeNames, entity, responses, callback); + } + } + + var aggregateNodeInfo = function (nodeNames, entity, responses, callback) { + //QDR.log.debug("got all results for " + entity); + // aggregate the responses + var newResponse = {}; + var thisNode = responses[selectedNodeId]; + newResponse['attributeNames'] = thisNode.attributeNames; + newResponse['results'] = thisNode.results; + newResponse['aggregates'] = []; + for (var i=0; i<thisNode.results.length; ++i) { + var result = thisNode.results[i]; + var vals = []; + result.forEach( function (val) { + vals.push({sum: val, detail: []}) + }) + newResponse.aggregates.push(vals); + } + var nameIndex = thisNode.attributeNames.indexOf("name"); + var ent = self.schema.entityTypes[entity]; + var ids = Object.keys(responses); + ids.sort(); + ids.forEach( function (id) { + var response = responses[id]; + var results = response.results; + results.forEach( function (result) { + // find the matching result in the aggregates + var found = newResponse.aggregates.some( function (aggregate, j) { + if (aggregate[nameIndex].sum === result[nameIndex]) { + // result and aggregate are now the same record, add the graphable values + newResponse.attributeNames.forEach( function (key, i) { + if (ent.attributes[key] && ent.attributes[key].graph) { + if (id != selectedNodeId) + aggregate[i].sum += result[i]; + aggregate[i].detail.push({node: self.nameFromId(id)+':', val: result[i]}) + } + }) + return true; // stop looping + } + return false; // continute looking for the aggregate record + }) + if (!found) { + // this attribute was not found in the aggregates yet + // because it was not in the selectedNodeId's results + var vals = []; + result.forEach( function (val) { + vals.push({sum: val, detail: []}) + }) + newResponse.aggregates.push(vals) + } + }) + }) + callback(nodeNames, entity, newResponse); + } + + nodeNames.forEach( function (id) { + self.getNodeInfo(id, '.'+entity, attrs, gotNodesResult); + }) + //TODO: implement a timeout in case not all requests complete + }, + + getSchema: function () { + //QDR.log.debug("getting schema"); + var ret; + self.correlator.request( + ret = self.sendMgmtQuery('GET-SCHEMA') + ).then(ret.id, function(response) { + //QDR.log.debug("Got schema response"); + self.schema = response; + //self.schema = angular.copy(response); + //self.topology.cleanUp(response); + self.notifyTopologyDone(); + }, ret.error); + }, + + sendQuery: function(toAddr, entity, attrs) { + var toAddrParts = toAddr.split('/'); + if (toAddrParts.shift() != "amqp:") { + self.topology.error(Error("unexpected format for router address: " + toAddr)); + return; + } + var fullAddr = self.toAddress + "/" + toAddrParts.join('/'); + + var body; + if (attrs) + body = { + "attributeNames": attrs, + } + else + body = { + "attributeNames": [], + } + + return self._send(body, fullAddr, "QUERY", "org.apache.qpid.dispatch" + entity); + }, + + sendMgmtQuery: function (operation) { + // TODO: file bug against dispatch - We should be able to just pass body: [], but that generates an 'invalid body' + return self._send([' '], self.toAddress + "/$management", operation); + }, + + _send: function (body, to, operation, entityType) { + var ret = {id: self.correlator.corr()}; + if (!self.sender || !self.sendable) { + ret.error = "no sender" + return ret; + } + try { + var application_properties = { + operation: operation, + type: "org.amqp.management", + name: "self" + }; + if (entityType) + application_properties.entityType = entityType; + + self.sender.send({ + body: body, + properties: { + to: to, + reply_to: self.receiver.remote.attach.source.address, + correlation_id: ret.id + }, + application_properties: application_properties + }) + } + catch (e) { + error = "error sending: " + e; + QDR.log.error(error) + ret.error = error; + } + return ret; + }, + + disconnect: function() { + self.connection.close(); + self.errorText = "Disconnected." + }, + + connect: function(options) { + self.options = options; + self.topologyInitialized = false; + if (!self.connected) { + var okay = {connection: false, sender: false, receiver: false} + var port = options.port || 5673; + var baseAddress = options.address + ':' + port; + var ws = self.rhea.websocket_connect(WebSocket); + self.toAddress = "amqp://" + baseAddress; + self.connectionError = undefined; + + var stop = function (context) { + //self.stopUpdating(); + okay.sender = false; + okay.receiver = false; + okay.connected = false; + self.connected = false; + self.sender = null; + self.receiver = null; + self.sendable = false; + } + + var maybeStart = function () { + if (okay.connection && okay.sender && okay.receiver && self.sendable && !self.connected) { + QDR.log.info("okay to start") + self.connected = true; + self.connection = connection; + self.sender = sender; + self.receiver = receiver; + self.onSubscription(); + self.gotTopology = false; + } + } + var onDisconnect = function () { + QDR.log.warn("Disconnected"); + stop(); + self.executeDisconnectActions(); + } + + QDR.log.debug("****** calling rhea.connect ********") + var connection = self.rhea.connect({ + connection_details:ws('ws://' + baseAddress, ["binary", "AMQWSB10"]), + reconnect:true, + properties: {console_identifier: 'Dispatch console'} + }); + connection.on('connection_open', function (context) { + QDR.log.debug("connection_opened") + okay.connection = true; + okay.receiver = false; + okay.sender = false; + }) + connection.on('disconnected', function (context) { + onDisconnect(); + self.errorText = "Error: Connection failed" + self.executeDisconnectActions(); + self.connectionError = true; + }) + connection.on('connection_close', function (context) { + onDisconnect(); + self.errorText = "Disconnected" + }) + + var sender = connection.open_sender("/$management"); + sender.on('sender_open', function (context) { + QDR.log.debug("sender_opened") + okay.sender = true + maybeStart() + }) + sender.on('sendable', function (context) { + //QDR.log.debug("sendable") + self.sendable = true; + maybeStart(); + }) + + var receiver = connection.open_receiver({source: {dynamic: true}}); + receiver.on('receiver_open', function (context) { + QDR.log.debug("receiver_opened") + okay.receiver = true; + maybeStart() + }) + receiver.on("message", function (context) { + self.correlator.resolve(context); + }); + + } + } + } + return self; + }]); + + return QDR; +}(QDR || {})); + +(function() { + console.dump = function(object) { + if (window.JSON && window.JSON.stringify) + console.log(JSON.stringify(object)); + else + console.log(object); + }; +})(); http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js ---------------------------------------------------------------------- diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js b/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js new file mode 100644 index 0000000..4758066 --- /dev/null +++ b/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js @@ -0,0 +1,125 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ +/** + * @module QDR + */ +var QDR = (function (QDR) { + + /** + * @method SettingsController + * @param $scope + * @param QDRServer + * + * Controller that handles the QDR settings page + */ + QDR.module.controller("QDR.SettingsController", ['$scope', 'QDRService', '$location', function($scope, QDRService, $location) { + + $scope.connecting = false; + $scope.connectionError = false; + $scope.connectionErrorText = undefined; + $scope.forms = {}; + + $scope.formEntity = angular.fromJson(localStorage[QDR.SETTINGS_KEY]) || {address: '', port: '', username: '', password: '', autostart: false}; + $scope.formConfig = { + properties: { + address: { + //description: "Router address", + 'type': 'java.lang.String', + required: true + }, + port: { + //description: 'Router port', + 'type': 'Integer', + tooltip: 'Ports to connect to, by default 5673' + }, + /* + username: { + description: 'User Name', + 'type': 'java.lang.String' + }, + password: { + description: 'Password', + 'type': 'password' + }, + useSSL: { + description: 'SSL', + 'type': 'boolean' + },*/ + autostart: { + //description: 'Connect at startup', + 'type': 'boolean', + tooltip: 'Whether or not the connection should be started as soon as you log into hawtio' + } + } + }; + + $scope.$watch('formEntity', function(newValue, oldValue) { + if (newValue !== oldValue) { + localStorage[QDR.SETTINGS_KEY] = angular.toJson(newValue); + } + }, true); + + $scope.buttonText = function() { + if (QDRService.isConnected()) { + return "Disconnect"; + } else { + return "Connect"; + } + }; + + + $scope.connect = function() { + if (QDRService.connected) { + QDRService.disconnect(); + return; + } + + if ($scope.forms.settings.$valid) { + $scope.connectionError = false; + $scope.connecting = true; + console.log("attempting to connect"); + QDRService.addDisconnectAction(function() { + QDR.log.debug("disconnect action called"); + $scope.connecting = false; + $scope.connectionErrorText = QDRService.errorText; + $scope.connectionError = true; + $scope.$apply(); + }); + QDRService.addConnectAction(function() { + //QDR.log.debug("got connection notification"); + $scope.connecting = false; + + var searchObject = $location.search(); + var goto = "overview"; + if (searchObject.org) { + goto = searchObject.org; + } + //QDR.log.debug("location before the connect " + $location.path()); + $location.path(QDR.pluginRoot +"/" + goto); + //QDR.log.debug("location after the connect " + $location.path()); + $scope.$apply(); + }); + QDRService.connect($scope.formEntity); + } + }; + + }]); + + return QDR; +}(QDR || {})); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
