diff --git a/internal/web/actions/default/clusters/cluster/installManual.go b/internal/web/actions/default/clusters/cluster/installManual.go index 187a4401..3d49bf1e 100644 --- a/internal/web/actions/default/clusters/cluster/installManual.go +++ b/internal/web/actions/default/clusters/cluster/installManual.go @@ -19,7 +19,7 @@ func (this *InstallManualAction) Init() { func (this *InstallManualAction) RunGet(params struct { ClusterId int64 }) { - this.Data["leftMenuItems"] = LeftMenuItemsForInstall(params.ClusterId, "manual") + this.Data["leftMenuItems"] = LeftMenuItemsForInstall(this.AdminContext(), params.ClusterId, "manual") nodesResp, err := this.RPC().NodeRPC().FindAllNotInstalledNodesWithClusterId(this.AdminContext(), &pb.FindAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: params.ClusterId}) if err != nil { diff --git a/internal/web/actions/default/clusters/cluster/installNodes.go b/internal/web/actions/default/clusters/cluster/installNodes.go index 4a4669f6..bd7aec17 100644 --- a/internal/web/actions/default/clusters/cluster/installNodes.go +++ b/internal/web/actions/default/clusters/cluster/installNodes.go @@ -19,7 +19,7 @@ func (this *InstallNodesAction) Init() { func (this *InstallNodesAction) RunGet(params struct { ClusterId int64 }) { - this.Data["leftMenuItems"] = LeftMenuItemsForInstall(params.ClusterId, "register") + this.Data["leftMenuItems"] = LeftMenuItemsForInstall(this.AdminContext(), params.ClusterId, "register") clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: params.ClusterId}) if err != nil { diff --git a/internal/web/actions/default/clusters/cluster/installRemote.go b/internal/web/actions/default/clusters/cluster/installRemote.go index 46a1eebb..a5d4fa32 100644 --- a/internal/web/actions/default/clusters/cluster/installRemote.go +++ b/internal/web/actions/default/clusters/cluster/installRemote.go @@ -21,7 +21,7 @@ func (this *InstallRemoteAction) Init() { func (this *InstallRemoteAction) RunGet(params struct { ClusterId int64 }) { - this.Data["leftMenuItems"] = LeftMenuItemsForInstall(params.ClusterId, "install") + this.Data["leftMenuItems"] = LeftMenuItemsForInstall(this.AdminContext(), params.ClusterId, "install") nodesResp, err := this.RPC().NodeRPC().FindAllNotInstalledNodesWithClusterId(this.AdminContext(), &pb.FindAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: params.ClusterId}) if err != nil { diff --git a/internal/web/actions/default/clusters/cluster/node/status.go b/internal/web/actions/default/clusters/cluster/node/status.go index c7b25d17..2aba5c99 100644 --- a/internal/web/actions/default/clusters/cluster/node/status.go +++ b/internal/web/actions/default/clusters/cluster/node/status.go @@ -1,8 +1,8 @@ package node import ( - "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/maps" ) @@ -34,6 +34,7 @@ func (this *StatusAction) RunPost(params struct { "isOk": node.InstallStatus.IsOk, "updatedAt": node.InstallStatus.UpdatedAt, "error": node.InstallStatus.Error, + "errorCode": node.InstallStatus.ErrorCode, } } else { this.Data["installStatus"] = nil diff --git a/internal/web/actions/default/clusters/cluster/upgradeRemote.go b/internal/web/actions/default/clusters/cluster/upgradeRemote.go index 9bb4abd0..7259801b 100644 --- a/internal/web/actions/default/clusters/cluster/upgradeRemote.go +++ b/internal/web/actions/default/clusters/cluster/upgradeRemote.go @@ -21,7 +21,7 @@ func (this *UpgradeRemoteAction) Init() { func (this *UpgradeRemoteAction) RunGet(params struct { ClusterId int64 }) { - this.Data["leftMenuItems"] = LeftMenuItemsForInstall(params.ClusterId, "upgrade") + this.Data["leftMenuItems"] = LeftMenuItemsForInstall(this.AdminContext(), params.ClusterId, "upgrade") nodes := []maps.Map{} resp, err := this.RPC().NodeRPC().FindAllUpgradeNodesWithClusterId(this.AdminContext(), &pb.FindAllUpgradeNodesWithClusterIdRequest{NodeClusterId: params.ClusterId}) diff --git a/internal/web/actions/default/clusters/cluster/utils.go b/internal/web/actions/default/clusters/cluster/utils.go index 098a6a63..0e250917 100644 --- a/internal/web/actions/default/clusters/cluster/utils.go +++ b/internal/web/actions/default/clusters/cluster/utils.go @@ -1,12 +1,34 @@ package cluster import ( + "context" + "github.com/TeaOSLab/EdgeAdmin/internal/rpc" "github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/maps" + "strconv" ) // 安装升级相关的左侧菜单 -func LeftMenuItemsForInstall(clusterId int64, selectedItem string) []maps.Map { +func LeftMenuItemsForInstall(ctx context.Context, clusterId int64, selectedItem string) []maps.Map { + rpcClient, _ := rpc.SharedRPC() + countNotInstalled := int64(0) + countUpgrade := int64(0) + if rpcClient != nil { + { + resp, err := rpcClient.NodeRPC().CountAllNotInstalledNodesWithClusterId(ctx, &pb.CountAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: clusterId}) + if err == nil { + countNotInstalled = resp.Count + } + } + { + resp, err := rpcClient.NodeRPC().CountAllUpgradeNodesWithClusterId(ctx, &pb.CountAllUpgradeNodesWithClusterIdRequest{NodeClusterId: clusterId}) + if err == nil { + countUpgrade = resp.Count + } + } + } + return []maps.Map{ { "name": "手动安装", @@ -19,12 +41,12 @@ func LeftMenuItemsForInstall(clusterId int64, selectedItem string) []maps.Map { "isActive": selectedItem == "register", }, { - "name": "远程安装", + "name": "远程安装(" + strconv.FormatInt(countNotInstalled, 10) + ")", "url": "/clusters/cluster/installRemote?clusterId=" + numberutils.FormatInt64(clusterId), "isActive": selectedItem == "install", }, { - "name": "远程升级", + "name": "远程升级(" + strconv.FormatInt(countUpgrade, 10) + ")", "url": "/clusters/cluster/upgradeRemote?clusterId=" + numberutils.FormatInt64(clusterId), "isActive": selectedItem == "upgrade", }, diff --git a/web/public/js/components/common/checkbox.js b/web/public/js/components/common/checkbox.js index 9b0e1fca..b5bbab2e 100644 --- a/web/public/js/components/common/checkbox.js +++ b/web/public/js/components/common/checkbox.js @@ -29,6 +29,13 @@ Vue.component("checkbox", { this.$emit("input", this.newValue) } }, + watch: { + value: function (v) { + if (typeof v == "boolean") { + this.newValue = v + } + } + }, template: `
可用线路是根据集群设置的域名获取的,注意DNS服务商可能对这些线路有所限制。
+当前节点对应的DNS线路,可用线路是根据集群设置的域名获取的,注意DNS服务商可能对这些线路有其他限制。
点击可已选中要使用的分组。
暂时还没有可以使用的分组。
+当前集群下暂时还没有可以使用的分组。
暂时没有需要远程安装的节点。
| 
+                         | 
 					节点名 | 访问IP | SSH地址 | @@ -18,8 +23,11 @@|||||
|---|---|---|---|---|---|---|---|---|
| 
+                     | 
 				
-					{{node.name}}
+					 | 
 				
 					{{addr.ip}}
diff --git a/web/views/@default/clusters/cluster/installRemote.js b/web/views/@default/clusters/cluster/installRemote.js
index 01b008e1..7fd030d4 100644
--- a/web/views/@default/clusters/cluster/installRemote.js
+++ b/web/views/@default/clusters/cluster/installRemote.js
@@ -1,87 +1,160 @@
 Tea.context(function () {
-	this.isInstalling = false
-	let installingNode = null
+    this.isInstalling = false
+    this.isBatch = false
+    let installingNode = null
 
-	this.$delay(function () {
-		this.reload()
-	})
+    this.nodes.forEach(function (v) {
+        v.isChecked = false
+    })
 
-	this.installNode = function (node) {
-		let that = this
-		teaweb.confirm("确定要开始安装此节点吗?", function () {
-			installingNode = node
-			that.isInstalling = true
-			node.isInstalling = true
+    this.$delay(function () {
+        this.reload()
+    })
 
-			that.$post("$")
-				.params({
-					nodeId: node.id
-				})
-		})
-	}
+    let that = this
 
-	this.reload = function () {
-		let that = this
-		if (installingNode != null) {
-			this.$post("/clusters/cluster/installStatus")
-				.params({
-					nodeId: installingNode.id
-				})
-				.success(function (resp) {
-					if (resp.data.status != null) {
-						installingNode.installStatus = resp.data.status
-						if (installingNode.installStatus.isFinished) {
-							if (installingNode.installStatus.isOk) {
-								installingNode = null
-								teaweb.success("安装成功", function () {
-									window.location.reload()
-								})
-							} else {
-								let nodeId = installingNode.id
-								let errMsg = installingNode.installStatus.error
-								that.isInstalling = false
-								installingNode.isInstalling = false
-								installingNode = null
+    this.checkNodes = function (isChecked) {
+        this.nodes.forEach(function (v) {
+            v.isChecked = isChecked
+        })
+    }
 
-								switch (resp.data.status.errorCode) {
-									case "EMPTY_LOGIN":
-									case "EMPTY_SSH_HOST":
-									case "EMPTY_SSH_PORT":
-									case "EMPTY_GRANT":
-										teaweb.warn("需要填写SSH登录信息", function () {
-											teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, {
-												callback: function () {
-													teaweb.reload()
-												}
-											})
-										})
-										return
-									case "CREATE_ROOT_DIRECTORY_FAILED":
-										teaweb.warn("创建根目录失败,请检查目录权限或者手工创建:" + errMsg)
-										return
-									case "INSTALL_HELPER_FAILED":
-										teaweb.warn("安装助手失败:" + errMsg)
-										return
-									case "TEST_FAILED":
-										teaweb.warn("环境测试失败:" + errMsg)
-										return
-									case "RPC_TEST_FAILED":
-										teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ", 现在修改API信息?", function () { - window.location = "/api" - }) - return - default: - teaweb.warn("安装失败:" + errMsg) - } - } - } - } - }) - .done(function () { - setTimeout(this.reload, 3000) - }) - } else { - setTimeout(this.reload, 3000) - } - } + this.countCheckedNodes = function () { + return that.nodes.$count(function (k, v) { + return v.isChecked + }) + } + + this.installNode = function (node) { + let that = this + if (this.isBatch) { + installingNode = node + that.isInstalling = true + node.isInstalling = true + + that.$post("$") + .params({ + nodeId: node.id + }) + } else { + teaweb.confirm("确定要开始安装此节点吗?", function () { + installingNode = node + that.isInstalling = true + node.isInstalling = true + + that.$post("$") + .params({ + nodeId: node.id + }) + }) + } + } + + this.installBatch = function () { + let that = this + this.isBatch = true + teaweb.confirm("确定要批量安装选中的节点吗?", function () { + that.installNext() + }) + } + + /** + * 安装下一个 + */ + this.installNext = function () { + let nextNode = this.nodes.$find(function (k, v) { + return v.isChecked + }) + + if (nextNode == null) { + teaweb.success("全部安装成功", function () { + teaweb.reload() + }) + } else { + this.installNode(nextNode) + } + return + } + + /** + * 重新加载状态 + */ + this.reload = function () { + let that = this + if (installingNode != null) { + this.$post("/clusters/cluster/installStatus") + .params({ + nodeId: installingNode.id + }) + .success(function (resp) { + if (resp.data.status != null) { + installingNode.installStatus = resp.data.status + if (installingNode.installStatus.isFinished) { + if (installingNode.installStatus.isOk) { + installingNode.isChecked = false // 取消选中 + installingNode = null + if (that.isBatch) { + that.installNext() + } else { + teaweb.success("安装成功", function () { + teaweb.reload() + }) + } + } else { + let nodeId = installingNode.id + let errMsg = installingNode.installStatus.error + that.isInstalling = false + installingNode.isInstalling = false + installingNode = null + + switch (resp.data.status.errorCode) { + case "EMPTY_LOGIN": + case "EMPTY_SSH_HOST": + case "EMPTY_SSH_PORT": + case "EMPTY_GRANT": + teaweb.warn("需要填写SSH登录信息", function () { + teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, { + callback: function () { + teaweb.reload() + } + }) + }) + return + case "SSH登录失败,请检查设置": + teaweb.warn("需要填写SSH登录信息", function () { + teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, { + callback: function () { + teaweb.reload() + } + }) + }) + return + case "CREATE_ROOT_DIRECTORY_FAILED": + teaweb.warn("创建根目录失败,请检查目录权限或者手工创建:" + errMsg) + return + case "INSTALL_HELPER_FAILED": + teaweb.warn("安装助手失败:" + errMsg) + return + case "TEST_FAILED": + teaweb.warn("环境测试失败:" + errMsg) + return + case "RPC_TEST_FAILED": + teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ", 现在修改API信息?", function () { + window.location = "/api" + }) + return + default: + teaweb.warn("安装失败:" + errMsg) + } + } + } + } + }) + .done(function () { + setTimeout(this.reload, 3000) + }) + } else { + setTimeout(this.reload, 3000) + } + } }) \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/installRemote.less b/web/views/@default/clusters/cluster/installRemote.less index f54837f3..ed56c29d 100644 --- a/web/views/@default/clusters/cluster/installRemote.less +++ b/web/views/@default/clusters/cluster/installRemote.less @@ -4,4 +4,14 @@ .right-box { top: 10em; +} + +h3 { + position: relative; +} + +h3 button { + position: absolute; + right: 1em; + top: -0.2em; } \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/install.js b/web/views/@default/clusters/cluster/node/install.js index 5357058a..b0492682 100644 --- a/web/views/@default/clusters/cluster/node/install.js +++ b/web/views/@default/clusters/cluster/node/install.js @@ -1,45 +1,103 @@ Tea.context(function () { - this.$delay(function () { - this.reloadStatus(this.nodeId) - }) + let isInstalling = false - // 开始安装 - this.install = function () { - this.$post("$") - .params({ - nodeId: this.nodeId - }) - .success(function () { + this.$delay(function () { + this.reloadStatus(this.nodeId) + }) - }) - } + // 开始安装 + this.install = function () { + isInstalling = true - // 设置节点安装状态 - this.updateNodeIsInstalled = function (isInstalled) { - teaweb.confirm("确定要将当前节点修改为未安装状态?", function () { - this.$post("/clusters/cluster/node/updateInstallStatus") - .params({ - nodeId: this.nodeId, - isInstalled: isInstalled ? 1 : 0 - }) - .refresh() - }) - } + this.$post("$") + .params({ + nodeId: this.nodeId + }) + .success(function () { - // 刷新状态 - this.reloadStatus = function (nodeId) { - this.$post("/clusters/cluster/node/status") - .params({ - nodeId: nodeId - }) - .success(function (resp) { - this.installStatus = resp.data.installStatus - this.node.isInstalled = resp.data.isInstalled - }) - .done(function () { - this.$delay(function () { - this.reloadStatus(nodeId) - }, 1000) - }); - } + }) + } + + // 设置节点安装状态 + this.updateNodeIsInstalled = function (isInstalled) { + teaweb.confirm("确定要将当前节点修改为未安装状态?", function () { + this.$post("/clusters/cluster/node/updateInstallStatus") + .params({ + nodeId: this.nodeId, + isInstalled: isInstalled ? 1 : 0 + }) + .refresh() + }) + } + + // 刷新状态 + this.reloadStatus = function (nodeId) { + let that = this + + this.$post("/clusters/cluster/node/status") + .params({ + nodeId: nodeId + }) + .success(function (resp) { + this.installStatus = resp.data.installStatus + this.node.isInstalled = resp.data.isInstalled + + if (!isInstalling) { + return + } + + let nodeId = this.node.id + let errMsg = this.installStatus.error + + if (this.installStatus.errorCode.length > 0) { + isInstalling = false + } + + switch (this.installStatus.errorCode) { + case "EMPTY_LOGIN": + case "EMPTY_SSH_HOST": + case "EMPTY_SSH_PORT": + case "EMPTY_GRANT": + teaweb.warn("需要填写SSH登录信息", function () { + teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, { + callback: function () { + that.install() + } + }) + }) + return + case "SSH_LOGIN_FAILED": + teaweb.warn("SSH登录失败,请检查设置", function () { + teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, { + callback: function () { + that.install() + } + }) + }) + return + case "CREATE_ROOT_DIRECTORY_FAILED": + teaweb.warn("创建根目录失败,请检查目录权限或者手工创建:" + errMsg) + return + case "INSTALL_HELPER_FAILED": + teaweb.warn("安装助手失败:" + errMsg) + return + case "TEST_FAILED": + teaweb.warn("环境测试失败:" + errMsg) + return + case "RPC_TEST_FAILED": + teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ", 现在修改API信息?", function () { + window.location = "/api" + }) + return + default: + shouldReload = true + //teaweb.warn("安装失败:" + errMsg) + } + }) + .done(function () { + this.$delay(function () { + this.reloadStatus(nodeId) + }, 1000) + }); + } }) \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/update.html b/web/views/@default/clusters/cluster/node/update.html index 5d0f8e7b..4dd7eef8 100644 --- a/web/views/@default/clusters/cluster/node/update.html +++ b/web/views/@default/clusters/cluster/node/update.html @@ -25,6 +25,7 @@  | 
 					
 					 当前节点对应的DNS线路,可用线路是根据集群设置的域名获取的,注意DNS服务商可能对这些线路有其他限制。  | 
 			|||||
| SSH主机端口 & | +SSH主机端口 * | 
 				
 				 比如22。 diff --git a/web/views/@default/clusters/cluster/upgradeRemote.css b/web/views/@default/clusters/cluster/upgradeRemote.css index 88ded090..86b4890d 100644 --- a/web/views/@default/clusters/cluster/upgradeRemote.css +++ b/web/views/@default/clusters/cluster/upgradeRemote.css @@ -4,4 +4,12 @@ .right-box { top: 10em; } +h3 { + position: relative; +} +h3 button { + position: absolute; + right: 1em; + top: -0.2em; +} /*# sourceMappingURL=upgradeRemote.css.map */ \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/upgradeRemote.css.map b/web/views/@default/clusters/cluster/upgradeRemote.css.map index 014a1b9d..dfafb58d 100644 --- a/web/views/@default/clusters/cluster/upgradeRemote.css.map +++ b/web/views/@default/clusters/cluster/upgradeRemote.css.map @@ -1 +1 @@ -{"version":3,"sources":["upgradeRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"upgradeRemote.css"} \ No newline at end of file +{"version":3,"sources":["upgradeRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA;;AAGD;EACC,kBAAA;;AAGD,EAAG;EACF,kBAAA;EACA,UAAA;EACA,WAAA","file":"upgradeRemote.css"} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/upgradeRemote.html b/web/views/@default/clusters/cluster/upgradeRemote.html index ae6df6b1..cec4ea28 100644 --- a/web/views/@default/clusters/cluster/upgradeRemote.html +++ b/web/views/@default/clusters/cluster/upgradeRemote.html @@ -6,10 +6,15 @@暂时没有需要升级的节点。 
-		 所有需要升级的节点+所有需要升级的节点 + +
  |