- Add cluster server list and assign page and client list page (for a specific app) Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -67,15 +67,15 @@ angular | |||
}) | |||
.state('dashboard.flowV1', { | |||
templateUrl: 'app/views/flow_old.html', | |||
url: '/v1/flow/:app', | |||
templateUrl: 'app/views/flow_v1.html', | |||
url: '/flow/:app', | |||
controller: 'FlowControllerV1', | |||
resolve: { | |||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||
return $ocLazyLoad.load({ | |||
name: 'sentinelDashboardApp', | |||
files: [ | |||
'app/scripts/controllers/flow_old.js', | |||
'app/scripts/controllers/flow_v1.js', | |||
] | |||
}); | |||
}] | |||
@@ -83,15 +83,15 @@ angular | |||
}) | |||
.state('dashboard.flow', { | |||
templateUrl: 'app/views/flow.html', | |||
url: '/flow/:app', | |||
controller: 'FlowController', | |||
templateUrl: 'app/views/flow_v2.html', | |||
url: '/v2/flow/:app', | |||
controller: 'FlowControllerV2', | |||
resolve: { | |||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||
return $ocLazyLoad.load({ | |||
name: 'sentinelDashboardApp', | |||
files: [ | |||
'app/scripts/controllers/flow.js', | |||
'app/scripts/controllers/flow_v2.js', | |||
] | |||
}); | |||
}] | |||
@@ -114,16 +114,64 @@ angular | |||
} | |||
}) | |||
.state('dashboard.clusterAll', { | |||
templateUrl: 'app/views/cluster.html', | |||
url: '/cluster/:app', | |||
controller: 'SentinelClusterController', | |||
.state('dashboard.clusterAppAssignManage', { | |||
templateUrl: 'app/views/cluster_app_assign_manage.html', | |||
url: '/cluster/assign_manage/:app', | |||
controller: 'SentinelClusterAppAssignManageController', | |||
resolve: { | |||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||
return $ocLazyLoad.load({ | |||
name: 'sentinelDashboardApp', | |||
files: [ | |||
'app/scripts/controllers/cluster.js', | |||
'app/scripts/controllers/cluster_app_assign_manage.js', | |||
] | |||
}); | |||
}] | |||
} | |||
}) | |||
.state('dashboard.clusterAppServerList', { | |||
templateUrl: 'app/views/cluster_app_server_list.html', | |||
url: '/cluster/server/:app', | |||
controller: 'SentinelClusterAppServerListController', | |||
resolve: { | |||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||
return $ocLazyLoad.load({ | |||
name: 'sentinelDashboardApp', | |||
files: [ | |||
'app/scripts/controllers/cluster_app_server_list.js', | |||
] | |||
}); | |||
}] | |||
} | |||
}) | |||
.state('dashboard.clusterAppClientList', { | |||
templateUrl: 'app/views/cluster_app_client_list.html', | |||
url: '/cluster/client/:app', | |||
controller: 'SentinelClusterAppTokenClientListController', | |||
resolve: { | |||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||
return $ocLazyLoad.load({ | |||
name: 'sentinelDashboardApp', | |||
files: [ | |||
'app/scripts/controllers/cluster_app_token_client_list.js', | |||
] | |||
}); | |||
}] | |||
} | |||
}) | |||
.state('dashboard.clusterSingle', { | |||
templateUrl: 'app/views/cluster_single_config.html', | |||
url: '/cluster/single/:app', | |||
controller: 'SentinelClusterSingleController', | |||
resolve: { | |||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||
return $ocLazyLoad.load({ | |||
name: 'sentinelDashboardApp', | |||
files: [ | |||
'app/scripts/controllers/cluster_single.js', | |||
] | |||
}); | |||
}] | |||
@@ -224,4 +272,4 @@ angular | |||
}] | |||
} | |||
}); | |||
}]); | |||
}]); |
@@ -0,0 +1,283 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog', | |||
'MachineService', 'ClusterStateService', | |||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { | |||
$scope.app = $stateParams.app; | |||
const UNSUPPORTED_CODE = 4041; | |||
const CLUSTER_MODE_CLIENT = 0; | |||
const CLUSTER_MODE_SERVER = 1; | |||
const DEFAULT_CLUSTER_SERVER_PORT = 18730; | |||
$scope.tmp = { | |||
curClientChosen: [], | |||
curRemainingClientChosen: [], | |||
curChosenServer: {}, | |||
}; | |||
function convertSetToString(set) { | |||
if (set === undefined) { | |||
return ''; | |||
} | |||
let s = ''; | |||
for (let i = 0; i < set.length; i++) { | |||
s = s + set[i]; | |||
if (i < set.length - 1) { | |||
s = s + ','; | |||
} | |||
} | |||
return s; | |||
} | |||
function convertStrToNamespaceSet(str) { | |||
if (str === undefined || str === '') { | |||
return []; | |||
} | |||
let arr = []; | |||
let spliced = str.split(','); | |||
spliced.forEach((v) => { | |||
arr.push(v.trim()); | |||
}); | |||
return arr; | |||
} | |||
function processAppSingleData(data) { | |||
if (data.state.server && data.state.server.namespaceSet) { | |||
data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet); | |||
data.mode = data.state.stateInfo.mode; | |||
} | |||
} | |||
function removeFromArr(arr, v) { | |||
for (let i = 0; i < arr.length; i++) { | |||
if (arr[i] === v) { | |||
arr.splice(i, 1); | |||
break; | |||
} | |||
} | |||
} | |||
function resetChosen() { | |||
$scope.tmp.curClientChosen = []; | |||
$scope.tmp.curRemainingClientChosen = []; | |||
} | |||
function generateMachineId(e) { | |||
return e.ip + '@' + e.commandPort; | |||
} | |||
function applyClusterMap(appClusterMachineList) { | |||
if (!appClusterMachineList) { | |||
return; | |||
} | |||
let tmpMap = new Map(); | |||
$scope.clusterMap = []; | |||
$scope.remainingClientAddressList = []; | |||
let tmpServerList = []; | |||
let tmpClientList = []; | |||
appClusterMachineList.forEach((e) => { | |||
if (e.mode === CLUSTER_MODE_CLIENT) { | |||
tmpClientList.push(e); | |||
} else if (e.mode === CLUSTER_MODE_SERVER) { | |||
tmpServerList.push(e); | |||
} else { | |||
$scope.remainingClientAddressList.push(generateMachineId(e)); | |||
} | |||
}); | |||
tmpServerList.forEach((e) => { | |||
let ip = e.ip; | |||
let machineId = ip + '@' + e.commandPort; | |||
let group = { | |||
ip: ip, | |||
machineId: machineId, | |||
port: e.state.server.port, | |||
clientSet: [], | |||
namespaceSetStr: e.state.server.namespaceSetStr, | |||
belongToApp: true, | |||
}; | |||
if (!tmpMap.has(ip)) { | |||
tmpMap.set(ip, group); | |||
} | |||
}); | |||
tmpClientList.forEach((e) => { | |||
let ip = e.ip; | |||
let machineId = ip + '@' + e.commandPort; | |||
let targetServer = e.state.client.clientConfig.serverHost; | |||
let targetPort = e.state.client.clientConfig.serverPort; | |||
if (targetServer === undefined || targetServer === '' || | |||
targetPort === undefined || targetPort <= 0) { | |||
$scope.remainingClientAddressList.push(generateMachineId(e)); | |||
return; | |||
} | |||
if (!tmpMap.has(targetServer)) { | |||
let group = { | |||
ip: targetServer, | |||
machineId: targetServer, | |||
port: targetPort, | |||
clientSet: [machineId], | |||
belongToApp: false, | |||
}; | |||
tmpMap.set(targetServer, group); | |||
} else { | |||
let g = tmpMap.get(targetServer); | |||
g.clientSet.push(machineId); | |||
} | |||
}); | |||
tmpMap.forEach((v) => { | |||
if (v !== undefined) { | |||
$scope.clusterMap.push(v); | |||
} | |||
}); | |||
} | |||
$scope.onCurrentServerChange = () => { | |||
resetChosen(); | |||
}; | |||
$scope.remainingClientAddressList = []; | |||
$scope.moveToServerGroup = () => { | |||
let chosenServer = $scope.tmp.curChosenServer; | |||
if (!chosenServer || !chosenServer.machineId) { | |||
return; | |||
} | |||
$scope.tmp.curRemainingClientChosen.forEach(e => { | |||
chosenServer.clientSet.push(e); | |||
removeFromArr($scope.remainingClientAddressList, e); | |||
}); | |||
resetChosen(); | |||
}; | |||
$scope.moveToRemainingSharePool = () => { | |||
$scope.tmp.curClientChosen.forEach(e => { | |||
$scope.remainingClientAddressList.push(e); | |||
removeFromArr($scope.tmp.curChosenServer.clientSet, e); | |||
}); | |||
resetChosen(); | |||
}; | |||
function parseIpFromMachineId(machineId) { | |||
if (machineId.indexOf('@') === -1) { | |||
return machineId; | |||
} | |||
let arr = machineId.split('@'); | |||
return arr[0]; | |||
} | |||
$scope.addToServerList = () => { | |||
let group; | |||
$scope.tmp.curRemainingClientChosen.forEach(e => { | |||
group = { | |||
machineId: e, | |||
ip: parseIpFromMachineId(e), | |||
port: DEFAULT_CLUSTER_SERVER_PORT, | |||
clientSet: [], | |||
namespaceSetStr: 'default,' + $scope.app, | |||
belongToApp: true, | |||
}; | |||
$scope.clusterMap.push(group); | |||
removeFromArr($scope.remainingClientAddressList, e); | |||
$scope.tmp.curChosenServer = group; | |||
}); | |||
resetChosen(); | |||
}; | |||
$scope.removeFromServerList = () => { | |||
let chosenServer = $scope.tmp.curChosenServer; | |||
if (!chosenServer || !chosenServer.machineId) { | |||
return; | |||
} | |||
chosenServer.clientSet.forEach((e) => { | |||
if (e !== undefined) { | |||
$scope.remainingClientAddressList.push(e); | |||
} | |||
}); | |||
if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) { | |||
$scope.remainingClientAddressList.push(chosenServer.machineId); | |||
} else { | |||
alert('提示:非本应用内机器将不会置回空闲列表中'); | |||
} | |||
removeFromArr($scope.clusterMap, chosenServer); | |||
resetChosen(); | |||
if ($scope.clusterMap.length > 0) { | |||
$scope.tmp.curChosenServer = $scope.clusterMap[0]; | |||
$scope.onCurrentServerChange(); | |||
} else { | |||
$scope.tmp.curChosenServer = {}; | |||
} | |||
}; | |||
function retrieveClusterAppInfo() { | |||
ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { | |||
if (data.code === 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.appClusterMachineList = data.data; | |||
$scope.appClusterMachineList.forEach(processAppSingleData); | |||
applyClusterMap($scope.appClusterMachineList); | |||
if ($scope.clusterMap.length > 0) { | |||
$scope.tmp.curChosenServer = $scope.clusterMap[0]; | |||
$scope.onCurrentServerChange(); | |||
} | |||
} else { | |||
$scope.appClusterMachineList = {}; | |||
if (data.code === UNSUPPORTED_CODE) { | |||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} | |||
} else { | |||
$scope.loadError = {message: data.msg}; | |||
} | |||
} | |||
}).error(() => { | |||
$scope.loadError = {message: '未知错误'}; | |||
}); | |||
} | |||
retrieveClusterAppInfo(); | |||
$scope.saveAndApplyAssign = () => { | |||
let ok = confirm('是否确认执行变更?'); | |||
if (!ok) { | |||
return; | |||
} | |||
let cm = $scope.clusterMap; | |||
if (!cm) { | |||
cm = []; | |||
} | |||
cm.forEach((e) => { | |||
e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr); | |||
}); | |||
cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr); | |||
let request = { | |||
clusterMap: cm, | |||
remainingList: $scope.remainingClientAddressList, | |||
}; | |||
ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => { | |||
if (data.code === 0 && data.data) { | |||
let failedServerSet = data.data.failedServerSet; | |||
let failedClientSet = data.data.failedClientSet; | |||
if (failedClientSet.length === 0 && failedServerSet.length === 0) { | |||
alert('全部推送成功'); | |||
} else { | |||
alert('推送完毕。token server 失败列表:' + JSON.stringify(failedServerSet) + | |||
'; token client 失败列表:' + JSON.stringify(failedClientSet)); | |||
} | |||
retrieveClusterAppInfo(); | |||
} else { | |||
if (data.code === UNSUPPORTED_CODE) { | |||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); | |||
} else { | |||
alert('推送失败:' + data.msg); | |||
} | |||
} | |||
}).error(() => { | |||
alert('未知错误'); | |||
}); | |||
}; | |||
}]); |
@@ -0,0 +1,543 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('SentinelClusterAppServerListController', ['$scope', '$stateParams', 'ngDialog', | |||
'MachineService', 'ClusterStateService', | |||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { | |||
$scope.app = $stateParams.app; | |||
const UNSUPPORTED_CODE = 4041; | |||
const CLUSTER_MODE_CLIENT = 0; | |||
const CLUSTER_MODE_SERVER = 1; | |||
const DEFAULT_CLUSTER_SERVER_PORT = 18730; | |||
const DEFAULT_NAMESPACE = 'default'; | |||
const DEFAULT_MAX_ALLOWED_QPS = 20000; | |||
// tmp for dialog temporary data. | |||
$scope.tmp = { | |||
curClientChosen: [], | |||
curRemainingClientChosen: [], | |||
curChosenServer: {}, | |||
}; | |||
$scope.remainingMachineList = []; | |||
function convertSetToString(set) { | |||
if (set === undefined) { | |||
return ''; | |||
} | |||
if (set.length === 1 && set[0] === DEFAULT_NAMESPACE) { | |||
return DEFAULT_NAMESPACE; | |||
} | |||
let s = ''; | |||
for (let i = 0; i < set.length; i++) { | |||
let ns = set[i]; | |||
if (ns !== DEFAULT_NAMESPACE) { | |||
s = s + ns; | |||
if (i < set.length - 1) { | |||
s = s + ','; | |||
} | |||
} | |||
} | |||
return s; | |||
} | |||
function convertStrToNamespaceSet(str) { | |||
if (str === undefined || str === '') { | |||
return []; | |||
} | |||
let arr = []; | |||
let spliced = str.split(','); | |||
spliced.forEach((v) => { | |||
arr.push(v.trim()); | |||
}); | |||
return arr; | |||
} | |||
function processAppSingleData(data) { | |||
if (data.state.server && data.state.server.namespaceSet) { | |||
data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet); | |||
data.mode = data.state.stateInfo.mode; | |||
} | |||
} | |||
function removeFromArr(arr, v) { | |||
for (let i = 0; i < arr.length; i++) { | |||
if (arr[i] === v) { | |||
arr.splice(i, 1); | |||
break; | |||
} | |||
} | |||
} | |||
function removeFromArrIf(arr, f) { | |||
for (let i = 0; i < arr.length; i++) { | |||
if (f(arr[i]) === true) { | |||
arr.splice(i, 1); | |||
break; | |||
} | |||
} | |||
} | |||
function resetAssignDialogChosen() { | |||
$scope.tmp.curClientChosen = []; | |||
$scope.tmp.curRemainingClientChosen = []; | |||
} | |||
function generateMachineId(e) { | |||
return e.ip + '@' + e.commandPort; | |||
} | |||
function applyClusterMap(appClusterMachineList) { | |||
if (!appClusterMachineList) { | |||
return; | |||
} | |||
let tmpMap = new Map(); | |||
$scope.clusterMap = []; | |||
$scope.remainingMachineList = []; | |||
let tmpServerList = []; | |||
let tmpClientList = []; | |||
appClusterMachineList.forEach((e) => { | |||
if (e.mode === CLUSTER_MODE_CLIENT) { | |||
tmpClientList.push(e); | |||
} else if (e.mode === CLUSTER_MODE_SERVER) { | |||
tmpServerList.push(e); | |||
} else { | |||
$scope.remainingMachineList.push(generateMachineId(e)); | |||
} | |||
}); | |||
tmpServerList.forEach((e) => { | |||
let ip = e.ip; | |||
let machineId = ip + '@' + e.commandPort; | |||
let group = { | |||
ip: ip, | |||
machineId: machineId, | |||
port: e.state.server.port, | |||
clientSet: [], | |||
namespaceSetStr: e.state.server.namespaceSetStr, | |||
maxAllowedQps: e.state.server.flow.maxAllowedQps, | |||
belongToApp: true, | |||
}; | |||
if (!tmpMap.has(ip)) { | |||
tmpMap.set(ip, group); | |||
} | |||
}); | |||
tmpClientList.forEach((e) => { | |||
let ip = e.ip; | |||
let machineId = ip + '@' + e.commandPort; | |||
let targetServer = e.state.client.clientConfig.serverHost; | |||
let targetPort = e.state.client.clientConfig.serverPort; | |||
if (targetServer === undefined || targetServer === '' || | |||
targetPort === undefined || targetPort <= 0) { | |||
$scope.remainingMachineList.push(generateMachineId(e)); | |||
return; | |||
} | |||
if (!tmpMap.has(targetServer)) { | |||
let group = { | |||
ip: targetServer, | |||
machineId: targetServer, | |||
port: targetPort, | |||
clientSet: [machineId], | |||
belongToApp: false, | |||
}; | |||
tmpMap.set(targetServer, group); | |||
} else { | |||
let g = tmpMap.get(targetServer); | |||
g.clientSet.push(machineId); | |||
} | |||
}); | |||
tmpMap.forEach((v) => { | |||
if (v !== undefined) { | |||
$scope.clusterMap.push(v); | |||
} | |||
}); | |||
} | |||
$scope.notChosenServer = (id) => { | |||
return id !== $scope.serverAssignDialogData.serverData.currentServer; | |||
}; | |||
$scope.onCurrentServerChange = () => { | |||
resetAssignDialogChosen(); | |||
}; | |||
$scope.moveToServerGroup = () => { | |||
$scope.tmp.curRemainingClientChosen.forEach(e => { | |||
$scope.serverAssignDialogData.serverData.clientSet.push(e); | |||
removeFromArr($scope.remainingMachineList, e); | |||
}); | |||
resetAssignDialogChosen(); | |||
}; | |||
$scope.moveToRemainingSharePool = () => { | |||
$scope.tmp.curClientChosen.forEach(e => { | |||
$scope.remainingMachineList.push(e); | |||
removeFromArr($scope.serverAssignDialogData.serverData.clientSet, e); | |||
}); | |||
resetAssignDialogChosen(); | |||
}; | |||
function parseIpFromMachineId(machineId) { | |||
if (machineId.indexOf('@') === -1) { | |||
return machineId; | |||
} | |||
let arr = machineId.split('@'); | |||
return arr[0]; | |||
} | |||
function retrieveClusterAssignInfoOfApp() { | |||
ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { | |||
if (data.code === 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.appClusterMachineList = data.data; | |||
$scope.appClusterMachineList.forEach(processAppSingleData); | |||
applyClusterMap($scope.appClusterMachineList); | |||
} else { | |||
$scope.appClusterMachineList = {}; | |||
if (data.code === UNSUPPORTED_CODE) { | |||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} | |||
} else { | |||
$scope.loadError = {message: data.msg}; | |||
} | |||
} | |||
}).error(() => { | |||
$scope.loadError = {message: '未知错误'}; | |||
}); | |||
} | |||
$scope.newServerDialog = () => { | |||
retrieveClusterAssignInfoOfApp(); | |||
$scope.serverAssignDialogData = { | |||
title: '新增 Token Server', | |||
type: 'add', | |||
confirmBtnText: '保存', | |||
serverData: { | |||
serverType: 0, | |||
clientSet: [], | |||
serverPort: DEFAULT_CLUSTER_SERVER_PORT, | |||
maxAllowedQps: DEFAULT_MAX_ALLOWED_QPS, | |||
} | |||
}; | |||
$scope.serverAssignDialog = ngDialog.open({ | |||
template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html', | |||
width: 1000, | |||
overlay: true, | |||
scope: $scope | |||
}); | |||
}; | |||
$scope.modifyServerAssignConfig = (id) => { | |||
ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { | |||
if (data.code === 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.appClusterMachineList = data.data; | |||
$scope.appClusterMachineList.forEach(processAppSingleData); | |||
applyClusterMap($scope.appClusterMachineList); | |||
let clusterMap = $scope.clusterMap; | |||
let d; | |||
for (let i = 0; i < clusterMap.length; i++) { | |||
if (clusterMap[i].machineId === id) { | |||
d = clusterMap[i]; | |||
} | |||
} | |||
if (!d) { | |||
alert('状态错误'); | |||
return; | |||
} | |||
$scope.serverAssignDialogData = { | |||
title: 'Token Server 分配编辑', | |||
type: 'edit', | |||
confirmBtnText: '保存', | |||
serverData: { | |||
currentServer: d.machineId, | |||
belongToApp: true, | |||
serverPort: d.port, | |||
clientSet: d.clientSet, | |||
} | |||
}; | |||
if (d.maxAllowedQps !== undefined) { | |||
$scope.serverAssignDialogData.serverData.maxAllowedQps = d.maxAllowedQps; | |||
} | |||
$scope.serverAssignDialog = ngDialog.open({ | |||
template: '/app/views/dialog/cluster/cluster-server-assign-dialog.html', | |||
width: 1000, | |||
overlay: true, | |||
scope: $scope | |||
}); | |||
} else { | |||
if (data.code === UNSUPPORTED_CODE) { | |||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} | |||
} else { | |||
$scope.loadError = {message: data.msg}; | |||
} | |||
} | |||
}).error(() => { | |||
$scope.loadError = {message: '未知错误'}; | |||
}); | |||
}; | |||
function getRemainingMachineList() { | |||
return $scope.remainingMachineList.filter((e) => $scope.notChosenServer(e)); | |||
} | |||
function doApplyNewSingleServerAssign() { | |||
let ok = confirm('是否确认执行变更?'); | |||
if (!ok) { | |||
return; | |||
} | |||
let serverData = $scope.serverAssignDialogData.serverData; | |||
let belongToApp = serverData.serverType == 0; // don't modify here! | |||
let machineId = serverData.currentServer; | |||
let request = { | |||
clusterMap: { | |||
machineId: machineId, | |||
ip: parseIpFromMachineId(machineId), | |||
port: serverData.serverPort, | |||
clientSet: serverData.clientSet, | |||
belongToApp: belongToApp, | |||
maxAllowedQps: serverData.maxAllowedQps, | |||
}, | |||
remainingList: getRemainingMachineList(), | |||
}; | |||
ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => { | |||
if (data.code === 0 && data.data) { | |||
let failedServerSet = data.data.failedServerSet; | |||
let failedClientSet = data.data.failedClientSet; | |||
if (failedClientSet.length === 0 && failedServerSet.length === 0) { | |||
alert('全部推送成功'); | |||
} else { | |||
let failedSet = []; | |||
if (failedServerSet) { | |||
failedServerSet.forEach((e) => { | |||
failedSet.push(e); | |||
}); | |||
} | |||
if (failedClientSet) { | |||
failedClientSet.forEach((e) => { | |||
failedSet.push(e); | |||
}); | |||
} | |||
alert('推送完毕。失败机器列表:' + JSON.stringify(failedSet)); | |||
} | |||
location.reload(); | |||
} else { | |||
if (data.code === UNSUPPORTED_CODE) { | |||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); | |||
} else { | |||
alert('推送失败:' + data.msg); | |||
} | |||
} | |||
}).error(() => { | |||
alert('未知错误'); | |||
}); | |||
} | |||
function doApplySingleServerAssignEdit() { | |||
let ok = confirm('是否确认执行变更?'); | |||
if (!ok) { | |||
return; | |||
} | |||
let serverData = $scope.serverAssignDialogData.serverData; | |||
let machineId = serverData.currentServer; | |||
let request = { | |||
clusterMap: { | |||
machineId: machineId, | |||
ip: parseIpFromMachineId(machineId), | |||
port: serverData.serverPort, | |||
clientSet: serverData.clientSet, | |||
belongToApp: serverData.belongToApp, | |||
}, | |||
remainingList: $scope.remainingMachineList, | |||
}; | |||
if (serverData.maxAllowedQps !== undefined) { | |||
request.clusterMap.maxAllowedQps = serverData.maxAllowedQps; | |||
} | |||
ClusterStateService.applyClusterSingleServerAssignOfApp($scope.app, request).success((data) => { | |||
if (data.code === 0 && data.data) { | |||
let failedServerSet = data.data.failedServerSet; | |||
let failedClientSet = data.data.failedClientSet; | |||
if (failedClientSet.length === 0 && failedServerSet.length === 0) { | |||
alert('全部推送成功'); | |||
} else { | |||
let failedSet = []; | |||
failedServerSet.forEach(failedSet.push); | |||
failedClientSet.forEach(failedSet.push); | |||
alert('推送完毕。失败机器列表:' + JSON.stringify(failedSet)); | |||
} | |||
location.reload(); | |||
} else { | |||
if (data.code === UNSUPPORTED_CODE) { | |||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); | |||
} else { | |||
alert('推送失败:' + data.msg); | |||
} | |||
} | |||
}).error(() => { | |||
alert('未知错误'); | |||
}); | |||
} | |||
$scope.saveAssignForDialog = () => { | |||
if (!checkAssignDialogValid()) { | |||
return; | |||
} | |||
if ($scope.serverAssignDialogData.type === 'add') { | |||
doApplyNewSingleServerAssign(); | |||
} else if ($scope.serverAssignDialogData.type === 'edit') { | |||
doApplySingleServerAssignEdit(); | |||
} else { | |||
alert('未知的操作'); | |||
} | |||
}; | |||
function checkAssignDialogValid() { | |||
let serverData = $scope.serverAssignDialogData.serverData; | |||
if (serverData.currentServer === undefined || serverData.currentServer === '') { | |||
alert('请指定有效的 Token Server'); | |||
return false; | |||
} | |||
if (serverData.serverPort === undefined || serverData.serverPort <= 0 || serverData.serverPort > 65535) { | |||
alert('请输入合法的端口值'); | |||
return false; | |||
} | |||
if (serverData.maxAllowedQps !== undefined && serverData.maxAllowedQps < 0) { | |||
alert('请输入合法的最大允许 QPS'); | |||
return false; | |||
} | |||
return true; | |||
} | |||
$scope.viewConnectionDetail = (serverVO) => { | |||
$scope.connectionDetailDialogData = { | |||
serverData: serverVO | |||
}; | |||
$scope.connectionDetailDialog = ngDialog.open({ | |||
template: '/app/views/dialog/cluster/cluster-server-connection-detail-dialog.html', | |||
width: 700, | |||
overlay: true, | |||
scope: $scope | |||
}); | |||
}; | |||
function generateRequestLimitDataStr(limitData) { | |||
if (limitData.length === 1 && limitData[0].namespace === DEFAULT_NAMESPACE) { | |||
return 'default: ' + limitData[0].currentQps + ' / ' + limitData[0].maxAllowedQps; | |||
} | |||
for (let i = 0; i < limitData.length; i++) { | |||
let crl = limitData[i]; | |||
if (crl.namespace === $scope.app) { | |||
return '' + crl.currentQps + ' / ' + crl.maxAllowedQps; | |||
} | |||
} | |||
return '0'; | |||
} | |||
function processServerListData(serverVO) { | |||
if (serverVO.state && serverVO.state.namespaceSet) { | |||
serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet); | |||
} | |||
if (serverVO.state && serverVO.state.requestLimitData) { | |||
serverVO.state.requestLimitDataStr = generateRequestLimitDataStr(serverVO.state.requestLimitData); | |||
} | |||
} | |||
$scope.generateConnectionSet = (data) => { | |||
let connectionSet = data; | |||
let s = ''; | |||
if (connectionSet) { | |||
s = s + '['; | |||
for (let i = 0; i < connectionSet.length; i++) { | |||
s = s + connectionSet[i].address; | |||
if (i < connectionSet.length - 1) { | |||
s = s + ', '; | |||
} | |||
} | |||
s = s + ']'; | |||
} else { | |||
s = '[]'; | |||
} | |||
return s; | |||
}; | |||
function retrieveClusterServerInfo() { | |||
ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) { | |||
if (data.code === 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.serverVOList = data.data; | |||
$scope.serverVOList.forEach(processServerListData); | |||
// if ($scope.serverVOList.length > 0) { | |||
// $scope.tmp.curChosenServer = $scope.serverVOList[0]; | |||
// $scope.onChosenServerChange(); | |||
// } | |||
} else { | |||
$scope.serverVOList = {}; | |||
if (data.code === UNSUPPORTED_CODE) { | |||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} | |||
} else { | |||
$scope.loadError = {message: data.msg}; | |||
} | |||
} | |||
}).error(() => { | |||
$scope.loadError = {message: '未知错误'}; | |||
}); | |||
} | |||
retrieveClusterServerInfo(); | |||
let confirmUnbindServerDialog; | |||
$scope.unbindServer = (id) => { | |||
$scope.pendingUnbindIds = [id]; | |||
$scope.confirmDialog = { | |||
title: '移除 Token Server', | |||
type: 'unbind_token_server', | |||
attentionTitle: '请确认是否移除以下 Token Server(该 server 下的 client 也会解除分配)', | |||
attention: id + '', | |||
confirmBtnText: '移除', | |||
}; | |||
confirmUnbindServerDialog = ngDialog.open({ | |||
template: '/app/views/dialog/confirm-dialog.html', | |||
scope: $scope, | |||
overlay: true | |||
}); | |||
}; | |||
function apiUnbindServerAssign(ids) { | |||
ClusterStateService.applyClusterServerBatchUnbind($scope.app, ids).success((data) => { | |||
if (data.code === 0 && data.data) { | |||
let failedServerSet = data.data.failedServerSet; | |||
let failedClientSet = data.data.failedClientSet; | |||
if (failedClientSet.length === 0 && failedServerSet.length === 0) { | |||
alert('成功'); | |||
} else { | |||
alert('操作推送完毕,部分失败机器列表:' + JSON.stringify(failedClientSet)); | |||
} | |||
location.reload(); | |||
} else { | |||
if (data.code === UNSUPPORTED_CODE) { | |||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); | |||
} else { | |||
alert('推送失败:' + data.msg); | |||
} | |||
} | |||
}).error(() => { | |||
alert('未知错误'); | |||
}); | |||
// confirmUnbindServerDialog.close(); | |||
} | |||
// Confirm function for confirm dialog. | |||
$scope.confirm = () => { | |||
if ($scope.confirmDialog.type === 'unbind_token_server') { | |||
apiUnbindServerAssign($scope.pendingUnbindIds); | |||
} else { | |||
console.error('Error dialog when unbinding token server'); | |||
} | |||
}; | |||
}]); |
@@ -0,0 +1,283 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('SentinelClusterAppAssignManageController', ['$scope', '$stateParams', 'ngDialog', | |||
'MachineService', 'ClusterStateService', | |||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { | |||
$scope.app = $stateParams.app; | |||
const UNSUPPORTED_CODE = 4041; | |||
const CLUSTER_MODE_CLIENT = 0; | |||
const CLUSTER_MODE_SERVER = 1; | |||
const DEFAULT_CLUSTER_SERVER_PORT = 18730; | |||
$scope.tmp = { | |||
curClientChosen: [], | |||
curRemainingClientChosen: [], | |||
curChosenServer: {}, | |||
}; | |||
function convertSetToString(set) { | |||
if (set === undefined) { | |||
return ''; | |||
} | |||
let s = ''; | |||
for (let i = 0; i < set.length; i++) { | |||
s = s + set[i]; | |||
if (i < set.length - 1) { | |||
s = s + ','; | |||
} | |||
} | |||
return s; | |||
} | |||
function convertStrToNamespaceSet(str) { | |||
if (str === undefined || str === '') { | |||
return []; | |||
} | |||
let arr = []; | |||
let spliced = str.split(','); | |||
spliced.forEach((v) => { | |||
arr.push(v.trim()); | |||
}); | |||
return arr; | |||
} | |||
function processAppSingleData(data) { | |||
if (data.state.server && data.state.server.namespaceSet) { | |||
data.state.server.namespaceSetStr = convertSetToString(data.state.server.namespaceSet); | |||
data.mode = data.state.stateInfo.mode; | |||
} | |||
} | |||
function removeFromArr(arr, v) { | |||
for (let i = 0; i < arr.length; i++) { | |||
if (arr[i] === v) { | |||
arr.splice(i, 1); | |||
break; | |||
} | |||
} | |||
} | |||
function resetChosen() { | |||
$scope.tmp.curClientChosen = []; | |||
$scope.tmp.curRemainingClientChosen = []; | |||
} | |||
function generateMachineId(e) { | |||
return e.ip + '@' + e.commandPort; | |||
} | |||
function applyClusterMap(appClusterMachineList) { | |||
if (!appClusterMachineList) { | |||
return; | |||
} | |||
let tmpMap = new Map(); | |||
$scope.clusterMap = []; | |||
$scope.remainingClientAddressList = []; | |||
let tmpServerList = []; | |||
let tmpClientList = []; | |||
appClusterMachineList.forEach((e) => { | |||
if (e.mode === CLUSTER_MODE_CLIENT) { | |||
tmpClientList.push(e); | |||
} else if (e.mode === CLUSTER_MODE_SERVER) { | |||
tmpServerList.push(e); | |||
} else { | |||
$scope.remainingClientAddressList.push(generateMachineId(e)); | |||
} | |||
}); | |||
tmpServerList.forEach((e) => { | |||
let ip = e.ip; | |||
let machineId = ip + '@' + e.commandPort; | |||
let group = { | |||
ip: ip, | |||
machineId: machineId, | |||
port: e.state.server.port, | |||
clientSet: [], | |||
namespaceSetStr: e.state.server.namespaceSetStr, | |||
belongToApp: true, | |||
}; | |||
if (!tmpMap.has(ip)) { | |||
tmpMap.set(ip, group); | |||
} | |||
}); | |||
tmpClientList.forEach((e) => { | |||
let ip = e.ip; | |||
let machineId = ip + '@' + e.commandPort; | |||
let targetServer = e.state.client.clientConfig.serverHost; | |||
let targetPort = e.state.client.clientConfig.serverPort; | |||
if (targetServer === undefined || targetServer === '' || | |||
targetPort === undefined || targetPort <= 0) { | |||
$scope.remainingClientAddressList.push(generateMachineId(e)); | |||
return; | |||
} | |||
if (!tmpMap.has(targetServer)) { | |||
let group = { | |||
ip: targetServer, | |||
machineId: targetServer, | |||
port: targetPort, | |||
clientSet: [machineId], | |||
belongToApp: false, | |||
}; | |||
tmpMap.set(targetServer, group); | |||
} else { | |||
let g = tmpMap.get(targetServer); | |||
g.clientSet.push(machineId); | |||
} | |||
}); | |||
tmpMap.forEach((v) => { | |||
if (v !== undefined) { | |||
$scope.clusterMap.push(v); | |||
} | |||
}); | |||
} | |||
$scope.onCurrentServerChange = () => { | |||
resetChosen(); | |||
}; | |||
$scope.remainingClientAddressList = []; | |||
$scope.moveToServerGroup = () => { | |||
let chosenServer = $scope.tmp.curChosenServer; | |||
if (!chosenServer || !chosenServer.machineId) { | |||
return; | |||
} | |||
$scope.tmp.curRemainingClientChosen.forEach(e => { | |||
chosenServer.clientSet.push(e); | |||
removeFromArr($scope.remainingClientAddressList, e); | |||
}); | |||
resetChosen(); | |||
}; | |||
$scope.moveToRemainingSharePool = () => { | |||
$scope.tmp.curClientChosen.forEach(e => { | |||
$scope.remainingClientAddressList.push(e); | |||
removeFromArr($scope.tmp.curChosenServer.clientSet, e); | |||
}); | |||
resetChosen(); | |||
}; | |||
function parseIpFromMachineId(machineId) { | |||
if (machineId.indexOf('@') === -1) { | |||
return machineId; | |||
} | |||
let arr = machineId.split('@'); | |||
return arr[0]; | |||
} | |||
$scope.addToServerList = () => { | |||
let group; | |||
$scope.tmp.curRemainingClientChosen.forEach(e => { | |||
group = { | |||
machineId: e, | |||
ip: parseIpFromMachineId(e), | |||
port: DEFAULT_CLUSTER_SERVER_PORT, | |||
clientSet: [], | |||
namespaceSetStr: 'default,' + $scope.app, | |||
belongToApp: true, | |||
}; | |||
$scope.clusterMap.push(group); | |||
removeFromArr($scope.remainingClientAddressList, e); | |||
$scope.tmp.curChosenServer = group; | |||
}); | |||
resetChosen(); | |||
}; | |||
$scope.removeFromServerList = () => { | |||
let chosenServer = $scope.tmp.curChosenServer; | |||
if (!chosenServer || !chosenServer.machineId) { | |||
return; | |||
} | |||
chosenServer.clientSet.forEach((e) => { | |||
if (e !== undefined) { | |||
$scope.remainingClientAddressList.push(e); | |||
} | |||
}); | |||
if (chosenServer.belongToApp || chosenServer.machineId.indexOf('@') !== -1) { | |||
$scope.remainingClientAddressList.push(chosenServer.machineId); | |||
} else { | |||
alert('提示:非本应用内机器将不会置回空闲列表中'); | |||
} | |||
removeFromArr($scope.clusterMap, chosenServer); | |||
resetChosen(); | |||
if ($scope.clusterMap.length > 0) { | |||
$scope.tmp.curChosenServer = $scope.clusterMap[0]; | |||
$scope.onCurrentServerChange(); | |||
} else { | |||
$scope.tmp.curChosenServer = {}; | |||
} | |||
}; | |||
function retrieveClusterAppInfo() { | |||
ClusterStateService.fetchClusterUniversalStateOfApp($scope.app).success(function (data) { | |||
if (data.code === 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.appClusterMachineList = data.data; | |||
$scope.appClusterMachineList.forEach(processAppSingleData); | |||
applyClusterMap($scope.appClusterMachineList); | |||
if ($scope.clusterMap.length > 0) { | |||
$scope.tmp.curChosenServer = $scope.clusterMap[0]; | |||
$scope.onCurrentServerChange(); | |||
} | |||
} else { | |||
$scope.appClusterMachineList = {}; | |||
if (data.code === UNSUPPORTED_CODE) { | |||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} | |||
} else { | |||
$scope.loadError = {message: data.msg}; | |||
} | |||
} | |||
}).error(() => { | |||
$scope.loadError = {message: '未知错误'}; | |||
}); | |||
} | |||
retrieveClusterAppInfo(); | |||
$scope.saveAndApplyAssign = () => { | |||
let ok = confirm('是否确认执行变更?'); | |||
if (!ok) { | |||
return; | |||
} | |||
let cm = $scope.clusterMap; | |||
if (!cm) { | |||
cm = []; | |||
} | |||
cm.forEach((e) => { | |||
e.namespaceSet = convertStrToNamespaceSet(e.namespaceSetStr); | |||
}); | |||
cm.namespaceSet = convertStrToNamespaceSet(cm.namespaceSetStr); | |||
let request = { | |||
clusterMap: cm, | |||
remainingList: $scope.remainingClientAddressList, | |||
}; | |||
ClusterStateService.applyClusterFullAssignOfApp($scope.app, request).success((data) => { | |||
if (data.code === 0 && data.data) { | |||
let failedServerSet = data.data.failedServerSet; | |||
let failedClientSet = data.data.failedClientSet; | |||
if (failedClientSet.length === 0 && failedServerSet.length === 0) { | |||
alert('全部推送成功'); | |||
} else { | |||
alert('推送完毕。token server 失败列表:' + JSON.stringify(failedServerSet) + | |||
'; token client 失败列表:' + JSON.stringify(failedClientSet)); | |||
} | |||
retrieveClusterAppInfo(); | |||
} else { | |||
if (data.code === UNSUPPORTED_CODE) { | |||
alert('该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'); | |||
} else { | |||
alert('推送失败:' + data.msg); | |||
} | |||
} | |||
}).error(() => { | |||
alert('未知错误'); | |||
}); | |||
}; | |||
}]); |
@@ -0,0 +1,97 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('SentinelClusterAppServerMonitorController', ['$scope', '$stateParams', 'ngDialog', | |||
'MachineService', 'ClusterStateService', | |||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { | |||
$scope.app = $stateParams.app; | |||
const UNSUPPORTED_CODE = 4041; | |||
const CLUSTER_MODE_SERVER = 1; | |||
$scope.tmp = { | |||
curChosenServer: {}, | |||
}; | |||
function convertSetToString(set) { | |||
if (set === undefined) { | |||
return ''; | |||
} | |||
let s = ''; | |||
for (let i = 0; i < set.length; i++) { | |||
s = s + set[i]; | |||
if (i < set.length - 1) { | |||
s = s + ','; | |||
} | |||
} | |||
return s; | |||
} | |||
function processServerData(serverVO) { | |||
if (serverVO.state && serverVO.state.namespaceSet) { | |||
serverVO.state.namespaceSetStr = convertSetToString(serverVO.state.namespaceSet); | |||
} | |||
} | |||
$scope.generateConnectionSet = (data) => { | |||
let connectionSet = data; | |||
let s = ''; | |||
if (connectionSet) { | |||
s = s + '['; | |||
for (let i = 0; i < connectionSet.length; i++) { | |||
s = s + connectionSet[i].address; | |||
if (i < connectionSet.length - 1) { | |||
s = s + ', '; | |||
} | |||
} | |||
s = s + ']'; | |||
} else { | |||
s = '[]'; | |||
} | |||
return s; | |||
}; | |||
$scope.onChosenServerChange = () => { | |||
}; | |||
function retrieveClusterServerInfo() { | |||
ClusterStateService.fetchClusterServerStateOfApp($scope.app).success(function (data) { | |||
if (data.code === 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.serverVOList = data.data; | |||
$scope.serverVOList.forEach(processServerData); | |||
if ($scope.serverVOList.length > 0) { | |||
$scope.tmp.curChosenServer = $scope.serverVOList[0]; | |||
$scope.onChosenServerChange(); | |||
} | |||
} else { | |||
$scope.serverVOList = {}; | |||
if (data.code === UNSUPPORTED_CODE) { | |||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} | |||
} else { | |||
$scope.loadError = {message: data.msg}; | |||
} | |||
} | |||
}).error(() => { | |||
$scope.loadError = {message: '未知错误'}; | |||
}); | |||
} | |||
retrieveClusterServerInfo(); | |||
$scope.macsInputConfig = { | |||
searchField: ['text', 'value'], | |||
persist: true, | |||
create: false, | |||
maxItems: 1, | |||
render: { | |||
item: function (data, escape) { | |||
return '<div>' + escape(data.text) + '</div>'; | |||
} | |||
}, | |||
onChange: function (value, oldValue) { | |||
$scope.macInputModel = value; | |||
} | |||
}; | |||
}]); |
@@ -0,0 +1,121 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('SentinelClusterAppTokenClientListController', ['$scope', '$stateParams', 'ngDialog', | |||
'MachineService', 'ClusterStateService', | |||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { | |||
$scope.app = $stateParams.app; | |||
const UNSUPPORTED_CODE = 4041; | |||
const CLUSTER_MODE_CLIENT = 0; | |||
const CLUSTER_MODE_SERVER = 1; | |||
function processClientData(clientVO) { | |||
} | |||
$scope.modifyClientConfigDialog = (clientVO) => { | |||
if (!clientVO) { | |||
return; | |||
} | |||
$scope.ccDialogData = { | |||
ip: clientVO.ip, | |||
commandPort: clientVO.commandPort, | |||
clientId: clientVO.id, | |||
serverHost: clientVO.state.clientConfig.serverHost, | |||
serverPort: clientVO.state.clientConfig.serverPort, | |||
requestTimeout: clientVO.state.clientConfig.requestTimeout, | |||
}; | |||
$scope.ccDialog = ngDialog.open({ | |||
template: '/app/views/dialog/cluster/cluster-client-config-dialog.html', | |||
width: 700, | |||
overlay: true, | |||
scope: $scope | |||
}); | |||
}; | |||
function checkValidClientConfig(config) { | |||
if (!config.serverHost || config.serverHost.trim() == '') { | |||
alert('请输入有效的 Token Server IP'); | |||
return false; | |||
} | |||
if (config.serverPort === undefined || config.serverPort <= 0 || config.serverPort > 65535) { | |||
alert('请输入有效的 Token Server 端口'); | |||
return false; | |||
} | |||
if (config.requestTimeout === undefined || config.requestTimeout <= 0) { | |||
alert('请输入有效的请求超时时长'); | |||
return false; | |||
} | |||
return true; | |||
} | |||
$scope.doModifyClientConfig = () => { | |||
if (!checkValidClientConfig($scope.ccDialogData)) { | |||
return; | |||
} | |||
let id = $scope.ccDialogData.id; | |||
let request = { | |||
app: $scope.app, | |||
ip: $scope.ccDialogData.ip, | |||
port: $scope.ccDialogData.commandPort, | |||
mode: CLUSTER_MODE_CLIENT, | |||
clientConfig: { | |||
serverHost: $scope.ccDialogData.serverHost, | |||
serverPort: $scope.ccDialogData.serverPort, | |||
requestTimeout: $scope.ccDialogData.requestTimeout, | |||
} | |||
}; | |||
ClusterStateService.modifyClusterConfig(request).success((data) => { | |||
if (data.code === 0 && data.data) { | |||
alert('修改 Token Client 配置成功'); | |||
window.location.reload(); | |||
} else { | |||
if (data.code === UNSUPPORTED_CODE) { | |||
alert('机器 ' + id + ' 的 Sentinel 没有引入集群限流客户端,请升级至 1.4.0 以上版本并引入相关依赖。'); | |||
} else { | |||
alert('修改失败:' + data.msg); | |||
} | |||
} | |||
}).error((data, header, config, status) => { | |||
alert('未知错误'); | |||
}); | |||
}; | |||
function retrieveClusterTokenClientInfo() { | |||
ClusterStateService.fetchClusterClientStateOfApp($scope.app) | |||
.success((data) => { | |||
if (data.code === 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.clientVOList = data.data; | |||
$scope.clientVOList.forEach(processClientData); | |||
} else { | |||
$scope.clientVOList = []; | |||
if (data.code === UNSUPPORTED_CODE) { | |||
$scope.loadError = {message: '该应用的 Sentinel 客户端不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} | |||
} else { | |||
$scope.loadError = {message: data.msg}; | |||
} | |||
} | |||
}) | |||
.error(() => { | |||
$scope.loadError = {message: '未知错误'}; | |||
}); | |||
} | |||
retrieveClusterTokenClientInfo(); | |||
$scope.macsInputConfig = { | |||
searchField: ['text', 'value'], | |||
persist: true, | |||
create: false, | |||
maxItems: 1, | |||
render: { | |||
item: function (data, escape) { | |||
return '<div>' + escape(data.text) + '</div>'; | |||
} | |||
}, | |||
onChange: function (value, oldValue) { | |||
$scope.macInputModel = value; | |||
} | |||
}; | |||
}]); |
@@ -1,6 +1,6 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('SentinelClusterController', ['$scope', '$stateParams', 'ngDialog', | |||
app.controller('SentinelClusterSingleController', ['$scope', '$stateParams', 'ngDialog', | |||
'MachineService', 'ClusterStateService', | |||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { | |||
$scope.app = $stateParams.app; | |||
@@ -39,7 +39,7 @@ app.controller('SentinelClusterController', ['$scope', '$stateParams', 'ngDialog | |||
} | |||
function convertStrToNamespaceSet(str) { | |||
if (str === undefined || str == '') { | |||
if (str === undefined || str === '') { | |||
return []; | |||
} | |||
let arr = []; | |||
@@ -51,11 +51,11 @@ app.controller('SentinelClusterController', ['$scope', '$stateParams', 'ngDialog | |||
} | |||
function fetchMachineClusterState() { | |||
if (!$scope.macInputModel) { | |||
if (!$scope.macInputModel || $scope.macInputModel === '') { | |||
return; | |||
} | |||
let mac = $scope.macInputModel.split(':'); | |||
ClusterStateService.fetchClusterUniversalState($scope.app, mac[0], mac[1]).success(function (data) { | |||
ClusterStateService.fetchClusterUniversalStateSingle($scope.app, mac[0], mac[1]).success(function (data) { | |||
if (data.code == 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.stateVO = data.data; | |||
@@ -144,6 +144,11 @@ app.controller('SentinelClusterController', ['$scope', '$stateParams', 'ngDialog | |||
alert('请输入有效的 Token Server 端口'); | |||
return false; | |||
} | |||
let flowConfig = stateVO.server.flow; | |||
if (flowConfig.maxAllowedQps === undefined || flowConfig.maxAllowedQps < 0) { | |||
alert('请输入有效的最大允许 QPS'); | |||
return false; | |||
} | |||
// if (transportConfig.idleSeconds === undefined || transportConfig.idleSeconds <= 0) { | |||
// alert('请输入有效的连接清理时长 (idleSeconds)'); | |||
// return false; | |||
@@ -205,19 +210,21 @@ app.controller('SentinelClusterController', ['$scope', '$stateParams', 'ngDialog | |||
function queryAppMachines() { | |||
MachineService.getAppMachines($scope.app).success( | |||
function (data) { | |||
if (data.code == 0) { | |||
if (data.code === 0) { | |||
// $scope.machines = data.data; | |||
if (data.data) { | |||
$scope.machines = []; | |||
$scope.macsInputOptionsOrigin = []; | |||
$scope.macsInputOptions = []; | |||
data.data.forEach(function (item) { | |||
if (item.health) { | |||
$scope.macsInputOptions.push({ | |||
$scope.macsInputOptionsOrigin.push({ | |||
text: item.ip + ':' + item.port, | |||
value: item.ip + ':' + item.port | |||
}); | |||
} | |||
}); | |||
$scope.macsInputOptions = $scope.macsInputOptionsOrigin; | |||
} | |||
if ($scope.macsInputOptions.length > 0) { | |||
$scope.macInputModel = $scope.macsInputOptions[0].value; | |||
@@ -227,11 +234,29 @@ app.controller('SentinelClusterController', ['$scope', '$stateParams', 'ngDialog | |||
} | |||
} | |||
); | |||
}; | |||
} | |||
queryAppMachines(); | |||
$scope.$watch('searchKey', function () { | |||
if (!$scope.macsInputOptions) { | |||
return; | |||
} | |||
if ($scope.searchKey) { | |||
$scope.macsInputOptions = $scope.macsInputOptionsOrigin | |||
.filter((e) => e.value.indexOf($scope.searchKey) !== -1); | |||
} else { | |||
$scope.macsInputOptions = $scope.macsInputOptionsOrigin; | |||
} | |||
if ($scope.macsInputOptions.length > 0) { | |||
$scope.macInputModel = $scope.macsInputOptions[0].value; | |||
} else { | |||
$scope.macInputModel = ''; | |||
} | |||
}); | |||
$scope.$watch('macInputModel', function () { | |||
if ($scope.macInputModel) { | |||
fetchMachineClusterState(); | |||
} | |||
}); | |||
queryAppMachines(); | |||
}]); |
@@ -1,6 +1,6 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('FlowController', ['$scope', '$stateParams', 'FlowServiceV2', 'ngDialog', | |||
app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', 'ngDialog', | |||
'MachineService', | |||
function ($scope, $stateParams, FlowService, ngDialog, | |||
MachineService) { | |||
@@ -27,6 +27,19 @@ app.controller('FlowController', ['$scope', '$stateParams', 'FlowServiceV2', 'ng | |||
} | |||
}; | |||
$scope.generateThresholdTypeShow = (rule) => { | |||
if (!rule.clusterMode) { | |||
return '单机'; | |||
} | |||
if (rule.clusterConfig.thresholdType === 0) { | |||
return '集群均摊'; | |||
} else if (rule.clusterConfig.thresholdType === 1) { | |||
return '集群总体'; | |||
} else { | |||
return '集群'; | |||
} | |||
}; | |||
getMachineRules(); | |||
function getMachineRules() { | |||
if (!$scope.macInputModel) { |
@@ -1,6 +1,6 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', 'ngDialog', | |||
app.controller('FlowControllerV2', ['$scope', '$stateParams', 'FlowServiceV2', 'ngDialog', | |||
'MachineService', | |||
function ($scope, $stateParams, FlowService, ngDialog, | |||
MachineService) { | |||
@@ -27,6 +27,19 @@ app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', ' | |||
} | |||
}; | |||
$scope.generateThresholdTypeShow = (rule) => { | |||
if (!rule.clusterMode) { | |||
return '单机'; | |||
} | |||
if (rule.clusterConfig.thresholdType === 0) { | |||
return '集群均摊'; | |||
} else if (rule.clusterConfig.thresholdType === 1) { | |||
return '集群总体'; | |||
} else { | |||
return '集群'; | |||
} | |||
}; | |||
getMachineRules(); | |||
function getMachineRules() { | |||
if (!$scope.macInputModel) { | |||
@@ -72,7 +85,11 @@ app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', ' | |||
app: $scope.app, | |||
ip: mac[0], | |||
port: mac[1], | |||
limitApp: 'default' | |||
limitApp: 'default', | |||
clusterMode: false, | |||
clusterConfig: { | |||
thresholdType: 0 | |||
} | |||
}; | |||
$scope.flowRuleDialog = { | |||
title: '新增流控规则', |
@@ -1,7 +1,7 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', | |||
'ngDialog', 'FlowServiceV2', 'DegradeService', 'AuthorityRuleService', 'ParamFlowService', 'MachineService', | |||
'ngDialog', 'FlowServiceV1', 'DegradeService', 'AuthorityRuleService', 'ParamFlowService', 'MachineService', | |||
'$interval', '$location', '$timeout', | |||
function ($scope, $stateParams, IdentityService, ngDialog, | |||
FlowService, DegradeService, AuthorityRuleService, ParamFlowService, MachineService, $interval, $location, $timeout) { | |||
@@ -52,6 +52,10 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', | |||
controlBehavior: 0, | |||
resource: resource, | |||
limitApp: 'default', | |||
clusterMode: false, | |||
clusterConfig: { | |||
thresholdType: 0 | |||
}, | |||
app: $scope.app, | |||
ip: mac[0], | |||
port: mac[1] | |||
@@ -89,13 +93,15 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', | |||
return; | |||
} | |||
FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) { | |||
if (data.code == 0) { | |||
if (data.code === 0) { | |||
flowRuleDialog.close(); | |||
let url = '/dashboard/flow/' + $scope.app; | |||
$location.path(url); | |||
} else { | |||
alert('失败!'); | |||
} | |||
}).error((data, header, config, status) => { | |||
alert('未知错误'); | |||
}); | |||
} | |||
@@ -105,7 +105,7 @@ angular.module('sentinelDashboardApp').controller('ParamFlowController', ['$scop | |||
let mac = $scope.macInputModel.split(':'); | |||
ParamFlowService.queryMachineRules($scope.app, mac[0], mac[1]) | |||
.success(function (data) { | |||
if (data.code == 0 && data.data) { | |||
if (data.code === 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.rules = data.data; | |||
$scope.rulesPageConfig.totalCount = $scope.rules.length; | |||
@@ -157,6 +157,10 @@ angular.module('sentinelDashboardApp').controller('ParamFlowController', ['$scop | |||
paramFlowItemList: [], | |||
count: 0, | |||
limitApp: 'default', | |||
clusterMode: false, | |||
clusterConfig: { | |||
thresholdType: 0 | |||
} | |||
} | |||
}; | |||
$scope.paramFlowRuleDialog = { | |||
@@ -57,8 +57,8 @@ | |||
<i class="glyphicon glyphicon-check"></i> 授权规则</a> | |||
</li> | |||
<li ui-sref-active="active"> | |||
<a ui-sref="dashboard.clusterAll({app: entry.app})"> | |||
<i class="glyphicon glyphicon-cloud"></i> 集群限流</a> | |||
<a ui-sref="dashboard.clusterAppServerList({app: entry.app})"> | |||
<i class="glyphicon glyphicon-cloud"></i> 集群流控</a> | |||
</li> | |||
<li ui-sref-active="active"> | |||
<a ui-sref="dashboard.machine({app: entry.app})"> | |||
@@ -1,28 +1,73 @@ | |||
/** | |||
* Parameter flow control service. | |||
* Cluster state control service. | |||
* | |||
* @author Eric Zhao | |||
*/ | |||
angular.module('sentinelDashboardApp').service('ClusterStateService', ['$http', function ($http) { | |||
this.fetchClusterUniversalState = function(app, ip, port) { | |||
this.fetchClusterUniversalStateSingle = function(app, ip, port) { | |||
var param = { | |||
app: app, | |||
ip: ip, | |||
port: port | |||
}; | |||
return $http({ | |||
url: '/cluster/state', | |||
url: '/cluster/state_single', | |||
params: param, | |||
method: 'GET' | |||
}); | |||
}; | |||
this.fetchClusterUniversalStateOfApp = function(app) { | |||
return $http({ | |||
url: '/cluster/state/' + app, | |||
method: 'GET' | |||
}); | |||
}; | |||
this.fetchClusterServerStateOfApp = function(app) { | |||
return $http({ | |||
url: '/cluster/server_state/' + app, | |||
method: 'GET' | |||
}); | |||
}; | |||
this.fetchClusterClientStateOfApp = function(app) { | |||
return $http({ | |||
url: '/cluster/client_state/' + app, | |||
method: 'GET' | |||
}); | |||
}; | |||
this.modifyClusterConfig = function(config) { | |||
return $http({ | |||
url: '/cluster/config/modify', | |||
url: '/cluster/config/modify_single', | |||
data: config, | |||
method: 'POST' | |||
}); | |||
}; | |||
this.applyClusterFullAssignOfApp = function(app, clusterMap) { | |||
return $http({ | |||
url: '/cluster/assign/all_server/' + app, | |||
data: clusterMap, | |||
method: 'POST' | |||
}); | |||
}; | |||
this.applyClusterSingleServerAssignOfApp = function(app, request) { | |||
return $http({ | |||
url: '/cluster/assign/single_server/' + app, | |||
data: request, | |||
method: 'POST' | |||
}); | |||
}; | |||
this.applyClusterServerBatchUnbind = function(app, machineSet) { | |||
return $http({ | |||
url: '/cluster/assign/unbind_server/' + app, | |||
data: machineSet, | |||
method: 'POST' | |||
}); | |||
}; | |||
}]); |
@@ -1,76 +1,76 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.service('FlowServiceV1', ['$http', function ($http) { | |||
this.queryMachineRules = function (app, ip, port) { | |||
var param = { | |||
app: app, | |||
ip: ip, | |||
port: port | |||
this.queryMachineRules = function (app, ip, port) { | |||
var param = { | |||
app: app, | |||
ip: ip, | |||
port: port | |||
}; | |||
return $http({ | |||
url: '/v1/flow/rules', | |||
params: param, | |||
method: 'GET' | |||
}); | |||
}; | |||
return $http({ | |||
url: '/v1/flow/rules', | |||
params: param, | |||
method: 'GET' | |||
}); | |||
}; | |||
this.newRule = function (rule) { | |||
var param = { | |||
resource: rule.resource, | |||
limitApp: rule.limitApp, | |||
grade: rule.grade, | |||
count: rule.count, | |||
strategy: rule.strategy, | |||
refResource: rule.refResource, | |||
controlBehavior: rule.controlBehavior, | |||
warmUpPeriodSec: rule.warmUpPeriodSec, | |||
maxQueueingTimeMs: rule.maxQueueingTimeMs, | |||
app: rule.app, | |||
ip: rule.ip, | |||
port: rule.port | |||
this.newRule = function (rule) { | |||
var param = { | |||
resource: rule.resource, | |||
limitApp: rule.limitApp, | |||
grade: rule.grade, | |||
count: rule.count, | |||
strategy: rule.strategy, | |||
refResource: rule.refResource, | |||
controlBehavior: rule.controlBehavior, | |||
warmUpPeriodSec: rule.warmUpPeriodSec, | |||
maxQueueingTimeMs: rule.maxQueueingTimeMs, | |||
app: rule.app, | |||
ip: rule.ip, | |||
port: rule.port | |||
}; | |||
return $http({ | |||
url: '/v1/flow/rule', | |||
data: rule, | |||
method: 'POST' | |||
}); | |||
}; | |||
return $http({ | |||
url: '/v1/flow/rule', | |||
data: rule, | |||
method: 'POST' | |||
}); | |||
}; | |||
this.saveRule = function (rule) { | |||
var param = { | |||
id: rule.id, | |||
resource: rule.resource, | |||
limitApp: rule.limitApp, | |||
grade: rule.grade, | |||
count: rule.count, | |||
strategy: rule.strategy, | |||
refResource: rule.refResource, | |||
controlBehavior: rule.controlBehavior, | |||
warmUpPeriodSec: rule.warmUpPeriodSec, | |||
maxQueueingTimeMs: rule.maxQueueingTimeMs, | |||
}; | |||
this.saveRule = function (rule) { | |||
var param = { | |||
id: rule.id, | |||
resource: rule.resource, | |||
limitApp: rule.limitApp, | |||
grade: rule.grade, | |||
count: rule.count, | |||
strategy: rule.strategy, | |||
refResource: rule.refResource, | |||
controlBehavior: rule.controlBehavior, | |||
warmUpPeriodSec: rule.warmUpPeriodSec, | |||
maxQueueingTimeMs: rule.maxQueueingTimeMs, | |||
return $http({ | |||
url: '/v1/flow/save.json', | |||
params: param, | |||
method: 'PUT' | |||
}); | |||
}; | |||
return $http({ | |||
url: '/v1/flow/save.json', | |||
params: param, | |||
method: 'PUT' | |||
}); | |||
}; | |||
this.deleteRule = function (rule) { | |||
var param = { | |||
id: rule.id, | |||
app: rule.app | |||
}; | |||
this.deleteRule = function (rule) { | |||
var param = { | |||
id: rule.id, | |||
app: rule.app | |||
return $http({ | |||
url: '/v1/flow/delete.json', | |||
params: param, | |||
method: 'DELETE' | |||
}); | |||
}; | |||
return $http({ | |||
url: '/v1/flow/delete.json', | |||
params: param, | |||
method: 'DELETE' | |||
}); | |||
}; | |||
function notNumberAtLeastZero(num) { | |||
return num === undefined || num === '' || isNaN(num) || num < 0; | |||
} | |||
@@ -79,7 +79,7 @@ app.service('FlowServiceV1', ['$http', function ($http) { | |||
return num === undefined || num === '' || isNaN(num) || num <= 0; | |||
} | |||
this.checkRuleValid = function (rule) { | |||
this.checkRuleValid = function (rule) { | |||
if (rule.resource === undefined || rule.resource === '') { | |||
alert('资源名称不能为空'); | |||
return false; | |||
@@ -110,6 +110,10 @@ app.service('FlowServiceV1', ['$http', function ($http) { | |||
alert('排队超时时间必须大于 0'); | |||
return false; | |||
} | |||
if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) { | |||
alert('集群限流配置不正确'); | |||
return false; | |||
} | |||
return true; | |||
}; | |||
}]); |
@@ -1,41 +1,41 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.service('FlowServiceV2', ['$http', function ($http) { | |||
this.queryMachineRules = function (app, ip, port) { | |||
var param = { | |||
app: app, | |||
ip: ip, | |||
port: port | |||
this.queryMachineRules = function (app, ip, port) { | |||
var param = { | |||
app: app, | |||
ip: ip, | |||
port: port | |||
}; | |||
return $http({ | |||
url: '/v2/flow/rules', | |||
params: param, | |||
method: 'GET' | |||
}); | |||
}; | |||
return $http({ | |||
url: '/v2/flow/rules', | |||
params: param, | |||
method: 'GET' | |||
}); | |||
}; | |||
this.newRule = function (rule) { | |||
return $http({ | |||
url: '/v2/flow/rule', | |||
data: rule, | |||
method: 'POST' | |||
}); | |||
}; | |||
this.newRule = function (rule) { | |||
return $http({ | |||
url: '/v2/flow/rule', | |||
data: rule, | |||
method: 'POST' | |||
}); | |||
}; | |||
this.saveRule = function (rule) { | |||
return $http({ | |||
url: '/v2/flow/rule/' + rule.id, | |||
data: rule, | |||
method: 'PUT' | |||
}); | |||
}; | |||
this.saveRule = function (rule) { | |||
return $http({ | |||
url: '/v2/flow/rule/' + rule.id, | |||
data: rule, | |||
method: 'PUT' | |||
}); | |||
}; | |||
this.deleteRule = function (rule) { | |||
return $http({ | |||
url: '/v2/flow/rule/' + rule.id, | |||
method: 'DELETE' | |||
}); | |||
}; | |||
this.deleteRule = function (rule) { | |||
return $http({ | |||
url: '/v2/flow/rule/' + rule.id, | |||
method: 'DELETE' | |||
}); | |||
}; | |||
function notNumberAtLeastZero(num) { | |||
return num === undefined || num === '' || isNaN(num) || num < 0; | |||
@@ -45,7 +45,7 @@ app.service('FlowServiceV2', ['$http', function ($http) { | |||
return num === undefined || num === '' || isNaN(num) || num <= 0; | |||
} | |||
this.checkRuleValid = function (rule) { | |||
this.checkRuleValid = function (rule) { | |||
if (rule.resource === undefined || rule.resource === '') { | |||
alert('资源名称不能为空'); | |||
return false; | |||
@@ -76,10 +76,10 @@ app.service('FlowServiceV2', ['$http', function ($http) { | |||
alert('排队超时时间必须大于 0'); | |||
return false; | |||
} | |||
if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) { | |||
alert('集群限流配置不正确'); | |||
return false; | |||
} | |||
if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) { | |||
alert('集群限流配置不正确'); | |||
return false; | |||
} | |||
return true; | |||
}; | |||
}]); |
@@ -525,6 +525,10 @@ body { | |||
max-width: 200px; | |||
} | |||
.width-200 { | |||
max-width: 200px; | |||
} | |||
.witdh-300 { | |||
max-width: 300px; | |||
} | |||
@@ -1,5 +1,13 @@ | |||
<div class="row clearfix"> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group" ng-if="stateVO.currentMode == 0"> | |||
<label class="col-sm-2 control-label">连接状态</label> | |||
<div class="col-sm-4"> | |||
<p class="form-control-static text-danger" ng-if="stateVO.client.clientConfig.clientState === 0">未连接</p> | |||
<p class="form-control-static" ng-if="stateVO.client.clientConfig.clientState === 1">连接中</p> | |||
<p class="form-control-static text-success" ng-if="stateVO.client.clientConfig.clientState === 2">已连接</p> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">Token Server IP</label> | |||
<div class="col-sm-4"> | |||
@@ -1,9 +1,16 @@ | |||
<div class="row clearfix"> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group" ng-if="stateVO.currentMode == 1"> | |||
<label class="col-sm-2 control-label">Token Server 模式</label> | |||
<div class="col-sm-4"> | |||
<p class="form-control-static" ng-if="!stateVO.server.embedded">独立模式 (Alone)</p> | |||
<p class="form-control-static" ng-if="stateVO.server.embedded">嵌入模式 (Embedded)</p> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">Token Server 端口</label> | |||
<div class="col-sm-4"> | |||
<input type="number" min="0" max="65535" required class="form-control highlight-border" ng-model='stateVO.server.transport.port' placeholder='请指定 Token Server 端口' /> | |||
<input type="number" min="1" max="65535" required class="form-control highlight-border" ng-model='stateVO.server.transport.port' placeholder='请指定 Token Server 端口' /> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
@@ -12,5 +19,11 @@ | |||
<input type="text" required class="form-control highlight-border" ng-model='stateVO.server.namespaceSetStr' placeholder='请指定服务端服务的命名空间集合(以,分隔)' /> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">最大全局 QPS</label> | |||
<div class="col-sm-4"> | |||
<input type="number" min="0" max="100000" required class="form-control highlight-border" ng-model='stateVO.server.flow.maxAllowedQps' placeholder='请指定服务端最大全局 QPS' /> | |||
</div> | |||
</div> | |||
</form> | |||
</div> |
@@ -0,0 +1,118 @@ | |||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;"> | |||
<div class="col-md-6" style="margin-bottom: 10px;"> | |||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||
</div> | |||
</div> | |||
<div class="separator"></div> | |||
<div class="container-fluid"> | |||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;"> | |||
<div class="col-md-12"> | |||
<div class="card"> | |||
<div class="inputs-header"> | |||
<span class="brand" style="font-size: 13px;">集群限流 - 机器分配/管控</span> | |||
</div> | |||
<!-- error panel --> | |||
<div class="row clearfix" ng-if="loadError"> | |||
<div class="col-md-6 col-md-offset-3"> | |||
<div class="panel panel-default"> | |||
<div class="panel-body"> | |||
<center> | |||
<p>{{loadError.message}}</p> | |||
</center> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!--.tools-header --> | |||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError"> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">Server 列表</label> | |||
<div class="col-sm-4"> | |||
<select ng-model="tmp.curChosenServer" ng-change="onCurrentServerChange()" size="8" | |||
ng-options="serverGroup.machineId for serverGroup in clusterMap" | |||
class="form-control"></select> | |||
</div> | |||
<button type="button" class="btn btn-outline-warning" ng-click="removeFromServerList()">移除 | |||
</button> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">Token Server 端口</label> | |||
<div class="col-sm-4"> | |||
<input type="number" class="form-control highlight-border" | |||
ng-disabled="!tmp.curChosenServer.belongToApp" | |||
ng-model='tmp.curChosenServer.port' placeholder='port' min="1" max="65535"/> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">命名空间集合</label> | |||
<div class="col-sm-4"> | |||
<input type="text" required class="form-control highlight-border" | |||
ng-disabled="!tmp.curChosenServer.belongToApp" | |||
ng-model='tmp.curChosenServer.namespaceSetStr' | |||
placeholder='请指定服务端服务的命名空间集合(以,分隔)'/> | |||
</div> | |||
</div> | |||
</form> | |||
<form role="form" class="form-inline" style="margin-top: 30px; margin-left: 20px;"> | |||
<div> | |||
<div class="form-group"> | |||
<div class="col-sm-12"> | |||
<label class="control-label" style="width: 200px; text-align: center;">当前对应客户端列表</label> | |||
<select size="8" multiple="multiple" ng-model="tmp.curClientChosen" | |||
ng-options="ip for ip in tmp.curChosenServer.clientSet" | |||
class="form-control" style="width: 100%;"></select> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<div class="col-sm-12"> | |||
<button type="button" class="btn btn-outline-primary" | |||
ng-disabled="!tmp.curChosenServer || !tmp.curChosenServer.machineId" | |||
ng-click="moveToServerGroup()">← | |||
</button> | |||
<button type="button" class="btn btn-outline-primary" | |||
ng-click="moveToRemainingSharePool()">→ | |||
</button> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<div class="col-sm-12"> | |||
<label class="control-label" style="width: 220px; text-align: center;">未分配机器列表</label> | |||
<div> | |||
<select size="8" multiple="multiple" ng-model="tmp.curRemainingClientChosen" | |||
ng-options="ip for ip in remainingClientAddressList" | |||
class="form-control" style="width: 100%;"> | |||
</select> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<div class="col-sm-6"> | |||
<button type="button" class="btn btn-outline-primary" | |||
ng-click="addToServerList()">添加为 server | |||
</button> | |||
</div> | |||
</div> | |||
</div> | |||
</form> | |||
<div class="separator"></div> | |||
<div style="margin-top: 20px;"> | |||
<button type="button" style="margin: 0 10px 10px 10px;" class="btn btn-outline-success" | |||
ng-click="saveAndApplyAssign()">保存并执行分配 | |||
</button> | |||
</div> | |||
</div> | |||
<!-- .card-body --> | |||
</div> | |||
<!-- .card --> | |||
</div> | |||
<!-- .col-md-12 --> | |||
</div> | |||
<!-- --> | |||
</div> | |||
<!-- .container-fluid --> |
@@ -0,0 +1,73 @@ | |||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;"> | |||
<div class="col-md-6" style="margin-bottom: 10px;"> | |||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||
</div> | |||
<div class="col-md-6"> | |||
<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.clusterAppServerList({app: app})"> | |||
Token Server 列表 | |||
</a> | |||
</div> | |||
</div> | |||
<div class="separator"></div> | |||
<div class="container-fluid"> | |||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;"> | |||
<div class="col-md-12"> | |||
<div class="card"> | |||
<div class="inputs-header"> | |||
<span class="brand" style="font-size: 13px;">集群限流 - Token Client 列表</span> | |||
</div> | |||
<!-- error panel --> | |||
<div class="row clearfix" ng-if="loadError"> | |||
<div class="col-md-6 col-md-offset-3"> | |||
<div class="panel panel-default"> | |||
<div class="panel-body"> | |||
<center> | |||
<p>{{loadError.message}}</p> | |||
</center> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!--.tools-header --> | |||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError"> | |||
<!-- table start --> | |||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;"> | |||
<thead> | |||
<tr style="background: #F3F5F7;"> | |||
<td style="min-width: 12%;">Client ID</td> | |||
<td>Server IP</td> | |||
<td>Server 端口</td> | |||
<td>连接状态</td> | |||
<td style="min-width: 15%;">操作</td> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<tr ng-repeat="clientVO in clientVOList"> | |||
<td style="word-wrap:break-word;word-break:break-all;">{{clientVO.id}}</td> | |||
<td style="word-wrap:break-word;word-break:break-all;">{{clientVO.state.clientConfig.serverHost}}</td> | |||
<td>{{clientVO.state.clientConfig.serverPort}}</td> | |||
<td> | |||
<span class="form-control-static text-danger" ng-if="clientVO.state.clientConfig.clientState === 0">未连接</span> | |||
<span class="form-control-static" ng-if="clientVO.state.clientConfig.clientState === 1">连接中</span> | |||
<span class="form-control-static text-success" ng-if="clientVO.state.clientConfig.clientState === 2">已连接</span> | |||
</td> | |||
<td> | |||
<button class="btn btn-xs btn-outline-primary" type="button" | |||
ng-click="modifyClientConfigDialog(clientVO)" style="font-size: 12px; height:25px;">编辑配置</button> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
<!-- .card-body --> | |||
</div> | |||
<!-- .card --> | |||
</div> | |||
<!-- .col-md-12 --> | |||
</div> | |||
<!-- --> | |||
</div> | |||
<!-- .container-fluid --> |
@@ -0,0 +1,87 @@ | |||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;"> | |||
<div class="col-md-6" style="margin-bottom: 10px;"> | |||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||
</div> | |||
<div class="col-md-6"> | |||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-if="!loadError" ng-click="newServerDialog()"> | |||
<i class="fa fa-plus"></i> 新增 Token Server</button> | |||
<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.clusterAppClientList({app: app})"> | |||
Token Client 列表 | |||
</a> | |||
</div> | |||
</div> | |||
<div class="separator"></div> | |||
<div class="container-fluid"> | |||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;"> | |||
<div class="col-md-12"> | |||
<div class="card"> | |||
<div class="inputs-header"> | |||
<span class="brand" style="font-size: 13px;">集群限流 - Token Server 列表</span> | |||
<input class="form-control width-200" placeholder="搜索 server..." ng-model="searchKey"> | |||
</div> | |||
<!-- error panel --> | |||
<div class="row clearfix" ng-if="loadError"> | |||
<div class="col-md-6 col-md-offset-3"> | |||
<div class="panel panel-default"> | |||
<div class="panel-body"> | |||
<center> | |||
<p>{{loadError.message}}</p> | |||
</center> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!--.tools-header --> | |||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError"> | |||
<!-- table start --> | |||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;"> | |||
<thead> | |||
<tr style="background: #F3F5F7;"> | |||
<td style="width: 15%;">Server ID</td> | |||
<td style="width: 10%;">Port</td> | |||
<td style="width: 15%;">命名空间集合</td> | |||
<td>总连接数</td> | |||
<td>QPS 总览</td> | |||
<td style="width: 20%;">操作</td> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<tr ng-repeat="serverVO in serverVOList | filter: {id: searchKey}"> | |||
<td style="word-wrap:break-word;word-break:break-all;">{{serverVO.id}}</td> | |||
<td>{{serverVO.port}}</td> | |||
<td style="word-wrap:break-word;word-break:break-all;"> | |||
{{serverVO.state.namespaceSetStr}} | |||
</td> | |||
<td style="word-wrap:break-word;word-break:break-all;"> | |||
{{serverVO.connectedCount}} | |||
</td> | |||
<td> | |||
{{serverVO.state.requestLimitDataStr}} | |||
<!--<p ng-repeat="crl in serverVO.state.requestLimitData">--> | |||
<!--<span ng-if="crl.namespace === app">{{crl.namespace}}:{{crl.currentQps}} / {{crl.maxAllowedQps}}</span>--> | |||
<!--</p>--> | |||
</td> | |||
<td> | |||
<button class="btn btn-xs btn-outline-primary" type="button" | |||
ng-click="viewConnectionDetail(serverVO)" style="font-size: 12px; height:25px;">连接详情</button> | |||
<button class="btn btn-xs btn-outline-primary" type="button" | |||
ng-click="modifyServerAssignConfig(serverVO.id)" style="font-size: 12px; height:25px;">管理</button> | |||
<button class="btn btn-xs btn-outline-danger" type="button" | |||
ng-click="unbindServer(serverVO.id)" style="font-size: 12px; height:25px;">移除</button> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
<!-- .card-body --> | |||
</div> | |||
<!-- .card --> | |||
</div> | |||
<!-- .col-md-12 --> | |||
</div> | |||
<!-- --> | |||
</div> | |||
<!-- .container-fluid --> |
@@ -0,0 +1,88 @@ | |||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;"> | |||
<div class="col-md-6" style="margin-bottom: 10px;"> | |||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||
</div> | |||
</div> | |||
<div class="separator"></div> | |||
<div class="container-fluid"> | |||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;"> | |||
<div class="col-md-12"> | |||
<div class="card"> | |||
<div class="inputs-header"> | |||
<span class="brand" style="font-size: 13px;">集群限流 - Token Server 总览</span> | |||
</div> | |||
<!-- error panel --> | |||
<div class="row clearfix" ng-if="loadError"> | |||
<div class="col-md-6 col-md-offset-3"> | |||
<div class="panel panel-default"> | |||
<div class="panel-body"> | |||
<center> | |||
<p>{{loadError.message}}</p> | |||
</center> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!--.tools-header --> | |||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError"> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group" hidden> | |||
<label class="col-sm-2 control-label">Token Server 列表</label> | |||
<div class="col-sm-4"> | |||
<select ng-model="tmp.curChosenServer" ng-change="onChosenServerChange()" | |||
ng-options="serverEntity.id for serverEntity in serverVOList" | |||
class="form-control"></select> | |||
</div> | |||
</div> | |||
</form> | |||
<!-- table start --> | |||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;"> | |||
<thead> | |||
<tr style="background: #F3F5F7;"> | |||
<td style="width: 12%;">Server ID</td> | |||
<td style="width: 5%;">Port</td> | |||
<td style="width: 10%;">命名空间集合</td> | |||
<td>总连接数</td> | |||
<td>连接情况</td> | |||
<td>QPS 总览</td> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<tr ng-repeat="serverVO in clientVOList"> | |||
<td style="word-wrap:break-word;word-break:break-all;">{{serverVO.id}}</td> | |||
<td>{{serverVO.port}}</td> | |||
<td style="word-wrap:break-word;word-break:break-all;"> | |||
{{serverVO.state.namespaceSetStr}} | |||
</td> | |||
<td style="word-wrap:break-word;word-break:break-all;"> | |||
{{serverVO.connectedCount}} | |||
</td> | |||
<td> | |||
<p ng-repeat="cg in serverVO.state.connection"> | |||
namespace: {{cg.namespace}}, 连接数: {{cg.connectedCount}}, clients: | |||
{{generateConnectionSet(cg.connectionSet)}} | |||
</p> | |||
</td> | |||
<td> | |||
<p ng-repeat="crl in serverVO.state.requestLimitData"> | |||
namespace: {{crl.namespace}}, 当前 QPS: {{crl.currentQps}}, 最大允许 QPS: | |||
{{crl.maxAllowedQps}} | |||
</p> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
<!-- .card-body --> | |||
</div> | |||
<!-- .card --> | |||
</div> | |||
<!-- .col-md-12 --> | |||
</div> | |||
<!-- --> | |||
</div> | |||
<!-- .container-fluid --> |
@@ -12,8 +12,7 @@ | |||
<div class="card"> | |||
<div class="inputs-header"> | |||
<span class="brand" style="font-size: 13px;">集群限流</span> | |||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button> | |||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey"> | |||
<input class="form-control witdh-200" placeholder="机器搜索" ng-model="searchKey"> | |||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;"> | |||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel" | |||
placeholder="机器"></selectize> | |||
@@ -34,8 +33,7 @@ | |||
</div> | |||
<!--.tools-header --> | |||
<div class="card-body" style="padding: 0px 0px;"> | |||
<!--<span class="brand" style="font-weight:bold;">集群限流状态</span>--> | |||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError"> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">当前模式</label> | |||
@@ -80,7 +78,8 @@ | |||
<div class="separator"></div> | |||
<div clss="row" style="margin-top: 20px;"> | |||
<button style="margin: 0 10px 10px 10px;" class="btn btn-outline-success" ng-click="saveConfig()">保存配置</button> | |||
<button style="margin: 0 10px 10px 10px;" class="btn btn-outline-success" | |||
ng-click="saveConfig()">保存配置</button> | |||
</div> | |||
</div> | |||
@@ -0,0 +1,40 @@ | |||
<div> | |||
<span class="brand" style="font-weight:bold;">修改 Token Client 配置</span> | |||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;"> | |||
<div class="panel-body"> | |||
<div class="row"> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group"> | |||
<label class="col-sm-3 control-label">Client ID</label> | |||
<div class="col-sm-4"> | |||
<p class="form-control-static">{{ccDialogData.clientId}}</p> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-3 control-label">Token Server IP</label> | |||
<div class="col-sm-4"> | |||
<input type="text" class="form-control highlight-border" ng-model='ccDialogData.serverHost' placeholder='请指定 Token Server IP' /> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-3 control-label">Token Server 端口</label> | |||
<div class="col-sm-4"> | |||
<input type="number" min="0" max="65535" required class="form-control highlight-border" ng-model='ccDialogData.serverPort' placeholder='请指定 Token Server 端口' /> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-3 control-label">请求超时时间(ms)</label> | |||
<div class="col-sm-4"> | |||
<input type="number" min="0" required class="form-control highlight-border" ng-model='ccDialogData.requestTimeout' placeholder='请指定请求超时时间(ms)' /> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
<div class="separator"></div> | |||
<div clss="row" style="margin-top: 20px;"> | |||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="ccDialog.close()">取消</button> | |||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="doModifyClientConfig()">保存</button> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,139 @@ | |||
<div> | |||
<span class="brand" style="font-weight:bold;">{{serverAssignDialogData.title}}</span> | |||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;"> | |||
<div class="panel-body"> | |||
<div class="row"> | |||
<form role="form" class="form-horizontal"> | |||
<div ng-if="serverAssignDialogData.type == 'edit'"> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">Token Server</label> | |||
<div class="col-sm-4"> | |||
<p class="form-control-static">{{serverAssignDialogData.serverData.currentServer}}</p> | |||
</div> | |||
<label class="col-sm-2 control-label">Server 端口</label> | |||
<div class="col-sm-3"> | |||
<input type="number" min="1" max="65535" class="form-control highlight-border" | |||
ng-disabled="!serverAssignDialogData.serverData.belongToApp" | |||
ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口'/> | |||
</div> | |||
</div> | |||
<div class="form-group" ng-if="serverAssignDialogData.serverData.belongToApp"> | |||
<label class="col-sm-2 control-label" | |||
title="server 最大允许的总 QPS,注意 embedded 模式下不要设的太大">最大允许 QPS</label> | |||
<div class="col-sm-3"> | |||
<input type="number" min="0" max="200000" class="form-control highlight-border" | |||
ng-model='serverAssignDialogData.serverData.maxAllowedQps' placeholder='请输入 server 最大允许 QPS'/> | |||
</div> | |||
</div> | |||
</div> | |||
<div ng-if="serverAssignDialogData.type == 'add'"> | |||
<div class="form-group" > | |||
<label class="col-sm-2 control-label">机器类型</label> | |||
<div class="col-sm-4"> | |||
<div class="form-control highlight-border" align="center"> | |||
<input type="radio" name="strategy" value="0" checked ng-model='serverAssignDialogData.serverData.serverType' /> 应用内机器 | |||
<input type="radio" name="strategy" value="1" ng-model='serverAssignDialogData.serverData.serverType' /> 外部指定机器 | |||
</div> | |||
</div> | |||
<div ng-if="serverAssignDialogData.serverData.serverType == 1"> | |||
<div class="col-sm-6"> | |||
<p class="form-control-static text-primary" style="font-size: x-small;">若指定外部 server,请先在相应页面对外部 server 进行配置,然后在此页面指定。</p> | |||
</div> | |||
</div> | |||
</div> | |||
<div ng-if="serverAssignDialogData.serverData.serverType == 0"> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">选择机器</label> | |||
<div class="col-sm-4"> | |||
<select ng-model="serverAssignDialogData.serverData.currentServer" ng-change="onCurrentServerChange()" | |||
ng-options="machineId for machineId in remainingMachineList" | |||
class="form-control"></select> | |||
</div> | |||
<label class="col-sm-2 control-label">Server 端口</label> | |||
<div class="col-sm-3"> | |||
<input type="number" min="1" max="65535" class="form-control highlight-border" | |||
ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口号'/> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label" | |||
title="server 最大允许的总 QPS,注意 embedded 模式下不要设的太大">最大允许 QPS</label> | |||
<div class="col-sm-3"> | |||
<input type="number" min="0" max="200000" class="form-control highlight-border" | |||
ng-model='serverAssignDialogData.serverData.maxAllowedQps' placeholder='请输入 server 最大允许 QPS'/> | |||
</div> | |||
</div> | |||
</div> | |||
<div ng-if="serverAssignDialogData.serverData.serverType == 1"> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">Server IP</label> | |||
<div class="col-sm-4"> | |||
<input type="text" class="form-control highlight-border" | |||
ng-model='serverAssignDialogData.serverData.currentServer' placeholder='请输入独立的 Token Server IP'/> | |||
</div> | |||
<label class="col-sm-2 control-label">Server 端口</label> | |||
<div class="col-sm-3"> | |||
<input type="number" min="1" max="65535" class="form-control highlight-border" | |||
ng-model='serverAssignDialogData.serverData.serverPort' placeholder='请输入 Token Server 端口号'/> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</form> | |||
<!-- assign form start --> | |||
<form role="form" class="form-inline" ng-if="serverAssignDialogData.serverData.currentServer" | |||
style="margin-top: 30px; margin-left: 20px; text-align: center;"> | |||
<div> | |||
<div class="form-group"> | |||
<div class="col-sm-12"> | |||
<label class="control-label" style="width: 220px; text-align: center;">请从中选取 client:</label> | |||
<div> | |||
<select size="8" multiple="multiple" ng-model="tmp.curRemainingClientChosen" | |||
ng-options="ip for ip in remainingMachineList | filter: notChosenServer" | |||
class="form-control" style="width: 100%;"> | |||
</select> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<div class="col-sm-12"> | |||
<button type="button" class="btn btn-outline-primary" | |||
ng-click="moveToRemainingSharePool()">← | |||
</button> | |||
<button type="button" class="btn btn-outline-primary" | |||
ng-click="moveToServerGroup()">→ | |||
</button> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<div class="col-sm-12"> | |||
<label class="control-label" style="width: 200px; text-align: center;">已选取的 client 列表</label> | |||
<div> | |||
<select size="8" multiple="multiple" ng-model="tmp.curClientChosen" | |||
ng-options="ip for ip in serverAssignDialogData.serverData.clientSet" | |||
class="form-control" style="width: 100%;"></select> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
<div class="separator"></div> | |||
<div clss="row" style="margin-top: 20px;"> | |||
<button class="btn btn-outline-danger" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="serverAssignDialog.close()">取消</button> | |||
<button class="btn btn-outline-success" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveAssignForDialog()">{{serverAssignDialogData.confirmBtnText}}</button> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,37 @@ | |||
<div> | |||
<span class="brand" style="font-weight:bold;">连接详情</span> | |||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;"> | |||
<div class="panel-body"> | |||
<div class="row"> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group"> | |||
<label class="col-sm-3 control-label">Token Server</label> | |||
<div class="col-sm-4"> | |||
<p class="form-control-static">{{connectionDetailDialogData.serverData.id}}</p> | |||
</div> | |||
</div> | |||
</form> | |||
<div class="col-md-12"> | |||
<!-- table start --> | |||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;"> | |||
<thead> | |||
<tr style="background: #F3F5F7;"> | |||
<td style="min-width: 15%;" class="text-center">命名空间</td> | |||
<td class="text-center">连接数</td> | |||
<td class="text-center">连接详情</td> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<tr ng-repeat="cg in connectionDetailDialogData.serverData.state.connection"> | |||
<td style="word-wrap:break-word;word-break:break-all;" class="text-center">{{cg.namespace}}</td> | |||
<td style="word-wrap:break-word;word-break:break-all;" class="text-center">{{cg.connectedCount}}</td> | |||
<td class="text-center">{{generateConnectionSet(cg.connectionSet)}}</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -15,7 +15,7 @@ | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label" title="流控针对应用,即流量入口的调用来源(origin)">流控应用</label> | |||
<label class="col-sm-2 control-label" data-toggle="tooltip" title="流控针对应用,即流量入口的调用来源(origin)">来源应用</label> | |||
<div class="col-sm-9"> | |||
<input type="text" class="form-control highlight-border" ng-model='currentRule.limitApp' placeholder='指调用方,"default"表示所有应用。' | |||
/> | |||
@@ -60,7 +60,7 @@ | |||
<div class="col-sm-4"> | |||
<div class="form-control highlight-border" align="center"> | |||
<input type="radio" name="clusterThresholdType" value="0" ng-model='currentRule.clusterConfig.thresholdType' /> 单机均摊 | |||
<input type="radio" name="clusterThresholdType" value="1" ng-model='currentRule.clusterConfig.thresholdType' /> Global | |||
<input type="radio" name="clusterThresholdType" value="1" ng-model='currentRule.clusterConfig.thresholdType' /> 总体阈值 | |||
</div> | |||
</div> | |||
</div> | |||
@@ -25,12 +25,42 @@ | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">限流阈值</label> | |||
<div class="col-sm-9"> | |||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.count' placeholder='请填入限流阈值' /> | |||
<div ng-if="!currentRule.rule.clusterMode"> | |||
<label class="col-sm-2 control-label">单机阈值</label> | |||
<div class="col-sm-9"> | |||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.count' placeholder='单机阈值' /> | |||
</div> | |||
</div> | |||
<div ng-if="currentRule.rule.clusterMode && currentRule.rule.clusterConfig.thresholdType == 0"> | |||
<label class="col-sm-2 control-label">均摊阈值</label> | |||
<div class="col-sm-9"> | |||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.count' placeholder='集群均摊阈值' /> | |||
</div> | |||
</div> | |||
<div ng-if="currentRule.rule.clusterMode && currentRule.rule.clusterConfig.thresholdType == 1"> | |||
<label class="col-sm-2 control-label">集群阈值</label> | |||
<div class="col-sm-9"> | |||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.count' placeholder='集群总体阈值' /> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">是否集群</label> | |||
<div class="col-sm-2"> | |||
<input type="checkbox" name="clusterMode" ng-model="currentRule.rule.clusterMode"> | |||
</div> | |||
<div ng-if="currentRule.rule.clusterMode"> | |||
<label class="col-sm-3 control-label">集群阈值模式</label> | |||
<div class="col-sm-4"> | |||
<div class="form-control highlight-border" align="center"> | |||
<input type="radio" name="clusterThresholdType" value="0" ng-model='currentRule.rule.clusterConfig.thresholdType' /> 单机均摊 | |||
<input type="radio" name="clusterThresholdType" value="1" ng-model='currentRule.rule.clusterConfig.thresholdType' /> 总体阈值 | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- exclusion item part start --> | |||
<div ng-if="!paramFlowRuleDialog.showAdvanceButton"> | |||
@@ -17,7 +17,7 @@ | |||
<input type="radio" name="grade" value="2" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> 线程数 | |||
<!--qps --> | |||
<input type="radio" name="grade" value="3" checked ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" | |||
/> QPS | |||
/> 入口 QPS | |||
</div> | |||
<div class="form-control highlight-border" ng-if="systemRuleDialog.type == 'add'" align="center"> | |||
<!--avgLoad --> | |||
@@ -28,7 +28,7 @@ | |||
<input type="radio" name="grade" value="2" ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" /> 线程数 | |||
<!--qps --> | |||
<input type="radio" name="grade" value="3" checked ng-model='currentRule.grade' ng-disabled="systemRuleDialog.type == 'edit'" | |||
/> QPS | |||
/> 入口 QPS | |||
</div> | |||
</div> | |||
</div> | |||
@@ -5,8 +5,8 @@ | |||
<div class="col-md-6"> | |||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()"> | |||
<i class="fa fa-plus"></i> 新增流控规则</button> | |||
<a class="btn btn-outline-success" style="float: right; margin-right: 10px;" ui-sref="dashboard.flow({app: app})"> | |||
回到新版</a> | |||
<!--<a class="btn btn-outline-success" style="float: right; margin-right: 10px;" ui-sref="dashboard.flow({app: app})">--> | |||
<!--回到集群页面</a>--> | |||
</div> | |||
</div> | |||
@@ -35,24 +35,24 @@ | |||
<td style="width: 40%"> | |||
资源名 | |||
</td> | |||
<td style="width: 10%;"> | |||
流控应用 | |||
<td style="width: 8%;"> | |||
来源应用 | |||
</td> | |||
<td style="width: 10%;"> | |||
<td style="width: 8%;"> | |||
流控模式 | |||
</td> | |||
<td style="width: 10%;"> | |||
<td style="width: 8%;"> | |||
阈值类型 | |||
</td> | |||
<td style="width: 10%;"> | |||
单机阈值 | |||
<td style="width: 6%;"> | |||
阈值 | |||
</td> | |||
<td style="width: 8%;"> | |||
阈值模式 | |||
</td> | |||
<td style="width: 10%;"> | |||
流控效果 | |||
</td> | |||
<!--<td style="width: 8%;">--> | |||
<!--状态--> | |||
<!--</td>--> | |||
<td style="width: 12%;"> | |||
操作 | |||
</td> | |||
@@ -74,10 +74,14 @@ | |||
<td style="word-wrap:break-word;word-break:break-all;"> | |||
{{rule.count}} | |||
</td> | |||
<td> | |||
<span>{{generateThresholdTypeShow(rule)}}</span> | |||
</td> | |||
<td> | |||
<span ng-if="rule.controlBehavior == 0">快速失败</span> | |||
<span ng-if="rule.controlBehavior == 1">Warm Up</span> | |||
<span ng-if="rule.controlBehavior == 2">排队等待</span> | |||
<span ng-if="rule.controlBehavior == 3">预热排队</span> | |||
</td> | |||
<td> | |||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(rule)" style="font-size: 12px; height:25px;">编辑</button> |
@@ -7,7 +7,8 @@ | |||
<i class="fa fa-plus"></i> 新增流控规则 | |||
</button> | |||
<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.flowV1({app: app})"> | |||
回到旧版(单机)</a> | |||
回到单机页面 | |||
</a> | |||
</div> | |||
</div> | |||
@@ -31,7 +32,7 @@ | |||
资源名 | |||
</td> | |||
<td style="width: 10%;"> | |||
流控应用 | |||
来源应用 | |||
</td> | |||
<td style="width: 8%;"> | |||
流控模式 | |||
@@ -40,10 +41,10 @@ | |||
阈值类型 | |||
</td> | |||
<td style="width: 8%;"> | |||
单机阈值 | |||
阈值 | |||
</td> | |||
<td style="width: 8%;"> | |||
是否集群 | |||
阈值模式 | |||
</td> | |||
<td style="width: 8%;"> | |||
流控效果 | |||
@@ -70,8 +71,7 @@ | |||
{{rule.count}} | |||
</td> | |||
<td> | |||
<span ng-if="rule.clusterMode">是</span> | |||
<span ng-if="!rule.clusterMode">否</span> | |||
<span>{{generateThresholdTypeShow(rule)}}</span> | |||
</td> | |||
<td> | |||
<span ng-if="rule.controlBehavior == 0">快速失败</span> |
@@ -53,7 +53,10 @@ | |||
流控模式 | |||
</td> | |||
<td style="width: 10%;"> | |||
单机阈值 | |||
阈值 | |||
</td> | |||
<td style="width: 8%;"> | |||
是否集群 | |||
</td> | |||
<td style="width: 10%;"> | |||
例外项数目 | |||
@@ -74,6 +77,10 @@ | |||
<td style="word-wrap:break-word;word-break:break-all;"> | |||
{{ruleEntity.rule.count}} | |||
</td> | |||
<td> | |||
<span ng-if="ruleEntity.rule.clusterMode">是</span> | |||
<span ng-if="!ruleEntity.rule.clusterMode">否</span> | |||
</td> | |||
<td> | |||
{{ruleEntity.rule.paramFlowItemList == undefined ? 0 : ruleEntity.rule.paramFlowItemList.length}} | |||
</td> | |||