可以批量远程安装和升级节点

This commit is contained in:
刘祥超
2021-01-31 16:03:52 +08:00
parent cc5a34c20e
commit 14636ed82f
24 changed files with 495 additions and 217 deletions

View File

@@ -19,7 +19,7 @@ func (this *InstallManualAction) Init() {
func (this *InstallManualAction) RunGet(params struct { func (this *InstallManualAction) RunGet(params struct {
ClusterId int64 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}) nodesResp, err := this.RPC().NodeRPC().FindAllNotInstalledNodesWithClusterId(this.AdminContext(), &pb.FindAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: params.ClusterId})
if err != nil { if err != nil {

View File

@@ -19,7 +19,7 @@ func (this *InstallNodesAction) Init() {
func (this *InstallNodesAction) RunGet(params struct { func (this *InstallNodesAction) RunGet(params struct {
ClusterId int64 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}) clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: params.ClusterId})
if err != nil { if err != nil {

View File

@@ -21,7 +21,7 @@ func (this *InstallRemoteAction) Init() {
func (this *InstallRemoteAction) RunGet(params struct { func (this *InstallRemoteAction) RunGet(params struct {
ClusterId int64 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}) nodesResp, err := this.RPC().NodeRPC().FindAllNotInstalledNodesWithClusterId(this.AdminContext(), &pb.FindAllNotInstalledNodesWithClusterIdRequest{NodeClusterId: params.ClusterId})
if err != nil { if err != nil {

View File

@@ -1,8 +1,8 @@
package node package node
import ( import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/maps"
) )
@@ -34,6 +34,7 @@ func (this *StatusAction) RunPost(params struct {
"isOk": node.InstallStatus.IsOk, "isOk": node.InstallStatus.IsOk,
"updatedAt": node.InstallStatus.UpdatedAt, "updatedAt": node.InstallStatus.UpdatedAt,
"error": node.InstallStatus.Error, "error": node.InstallStatus.Error,
"errorCode": node.InstallStatus.ErrorCode,
} }
} else { } else {
this.Data["installStatus"] = nil this.Data["installStatus"] = nil

View File

@@ -21,7 +21,7 @@ func (this *UpgradeRemoteAction) Init() {
func (this *UpgradeRemoteAction) RunGet(params struct { func (this *UpgradeRemoteAction) RunGet(params struct {
ClusterId int64 ClusterId int64
}) { }) {
this.Data["leftMenuItems"] = LeftMenuItemsForInstall(params.ClusterId, "upgrade") this.Data["leftMenuItems"] = LeftMenuItemsForInstall(this.AdminContext(), params.ClusterId, "upgrade")
nodes := []maps.Map{} nodes := []maps.Map{}
resp, err := this.RPC().NodeRPC().FindAllUpgradeNodesWithClusterId(this.AdminContext(), &pb.FindAllUpgradeNodesWithClusterIdRequest{NodeClusterId: params.ClusterId}) resp, err := this.RPC().NodeRPC().FindAllUpgradeNodesWithClusterId(this.AdminContext(), &pb.FindAllUpgradeNodesWithClusterIdRequest{NodeClusterId: params.ClusterId})

View File

@@ -1,12 +1,34 @@
package cluster package cluster
import ( import (
"context"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils" "github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps" "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{ return []maps.Map{
{ {
"name": "手动安装", "name": "手动安装",
@@ -19,12 +41,12 @@ func LeftMenuItemsForInstall(clusterId int64, selectedItem string) []maps.Map {
"isActive": selectedItem == "register", "isActive": selectedItem == "register",
}, },
{ {
"name": "远程安装", "name": "远程安装(" + strconv.FormatInt(countNotInstalled, 10) + ")",
"url": "/clusters/cluster/installRemote?clusterId=" + numberutils.FormatInt64(clusterId), "url": "/clusters/cluster/installRemote?clusterId=" + numberutils.FormatInt64(clusterId),
"isActive": selectedItem == "install", "isActive": selectedItem == "install",
}, },
{ {
"name": "远程升级", "name": "远程升级(" + strconv.FormatInt(countUpgrade, 10) + ")",
"url": "/clusters/cluster/upgradeRemote?clusterId=" + numberutils.FormatInt64(clusterId), "url": "/clusters/cluster/upgradeRemote?clusterId=" + numberutils.FormatInt64(clusterId),
"isActive": selectedItem == "upgrade", "isActive": selectedItem == "upgrade",
}, },

View File

@@ -29,6 +29,13 @@ Vue.component("checkbox", {
this.$emit("input", this.newValue) this.$emit("input", this.newValue)
} }
}, },
watch: {
value: function (v) {
if (typeof v == "boolean") {
this.newValue = v
}
}
},
template: `<div class="ui checkbox"> template: `<div class="ui checkbox">
<input type="checkbox" :name="name" :value="elementValue" :id="elementId" @change="change" v-model="newValue"/> <input type="checkbox" :name="name" :value="elementValue" :id="elementId" @change="change" v-model="newValue"/>
<label :for="elementId" style="font-size: 0.85em!important;"><slot></slot></label> <label :for="elementId" style="font-size: 0.85em!important;"><slot></slot></label>

View File

@@ -24,7 +24,7 @@
<td> <td>
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/> <input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
<dns-route-selector :v-all-routes="dnsRoutes"></dns-route-selector> <dns-route-selector :v-all-routes="dnsRoutes"></dns-route-selector>
<p class="comment">可用线路是根据集群设置的域名获取的注意DNS服务商可能对这些线路有限制。</p> <p class="comment">当前节点对应的DNS线路可用线路是根据集群设置的域名获取的注意DNS服务商可能对这些线路有其他限制。</p>
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@@ -13,7 +13,7 @@
<p class="comment">点击可已选中要使用的分组。</p> <p class="comment">点击可已选中要使用的分组。</p>
</div> </div>
<div v-else> <div v-else>
<p class="comment">暂时还没有可以使用的分组。</p> <p class="comment">当前集群下暂时还没有可以使用的分组。</p>
</div> </div>
</td> </td>
</tr> </tr>

View File

@@ -4,4 +4,12 @@
.right-box { .right-box {
top: 10em; top: 10em;
} }
h3 {
position: relative;
}
h3 button {
position: absolute;
right: 1em;
top: -0.2em;
}
/*# sourceMappingURL=installRemote.css.map */ /*# sourceMappingURL=installRemote.css.map */

View File

@@ -1 +1 @@
{"version":3,"sources":["installRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"installRemote.css"} {"version":3,"sources":["installRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA;;AAGD;EACC,kBAAA;;AAGD,EAAG;EACF,kBAAA;EACA,UAAA;EACA,WAAA","file":"installRemote.css"}

View File

@@ -6,10 +6,15 @@
<p class="comment" v-if="nodes.length == 0">暂时没有需要远程安装的节点。</p> <p class="comment" v-if="nodes.length == 0">暂时没有需要远程安装的节点。</p>
<div v-if="nodes.length > 0"> <div v-if="nodes.length > 0">
<h3>所有未安装节点</h3> <h3>所有未安装节点
<button class="ui button primary tiny" v-if="countCheckedNodes() > 0" @click.prevent="installBatch()">批量安装({{countCheckedNodes()}})</button>
</h3>
<table class="ui table selectable celled"> <table class="ui table selectable celled">
<thead> <thead>
<tr> <tr>
<th style="width:3em">
<checkbox @input="checkNodes"></checkbox>
</th>
<th>节点名</th> <th>节点名</th>
<th>访问IP</th> <th>访问IP</th>
<th>SSH地址</th> <th>SSH地址</th>
@@ -18,8 +23,11 @@
</tr> </tr>
</thead> </thead>
<tr v-for="node in nodes"> <tr v-for="node in nodes">
<td>
<checkbox v-model="node.isChecked" v-if="node.installStatus == null || !node.installStatus.isOk"></checkbox>
</td>
<td> <td>
<a :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">{{node.name}}</a> <link-icon :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">{{node.name}}</link-icon>
</td> </td>
<td> <td>
<span v-for="addr in node.addresses" v-if="addr.canAccess" class="ui label tiny">{{addr.ip}}</span> <span v-for="addr in node.addresses" v-if="addr.canAccess" class="ui label tiny">{{addr.ip}}</span>

View File

@@ -1,87 +1,160 @@
Tea.context(function () { Tea.context(function () {
this.isInstalling = false this.isInstalling = false
let installingNode = null this.isBatch = false
let installingNode = null
this.$delay(function () { this.nodes.forEach(function (v) {
this.reload() v.isChecked = false
}) })
this.installNode = function (node) { this.$delay(function () {
let that = this this.reload()
teaweb.confirm("确定要开始安装此节点吗?", function () { })
installingNode = node
that.isInstalling = true
node.isInstalling = true
that.$post("$") let that = this
.params({
nodeId: node.id
})
})
}
this.reload = function () { this.checkNodes = function (isChecked) {
let that = this this.nodes.forEach(function (v) {
if (installingNode != null) { v.isChecked = isChecked
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
switch (resp.data.status.errorCode) { this.countCheckedNodes = function () {
case "EMPTY_LOGIN": return that.nodes.$count(function (k, v) {
case "EMPTY_SSH_HOST": return v.isChecked
case "EMPTY_SSH_PORT": })
case "EMPTY_GRANT": }
teaweb.warn("需要填写SSH登录信息", function () {
teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, { this.installNode = function (node) {
callback: function () { let that = this
teaweb.reload() if (this.isBatch) {
} installingNode = node
}) that.isInstalling = true
}) node.isInstalling = true
return
case "CREATE_ROOT_DIRECTORY_FAILED": that.$post("$")
teaweb.warn("创建根目录失败,请检查目录权限或者手工创建:" + errMsg) .params({
return nodeId: node.id
case "INSTALL_HELPER_FAILED": })
teaweb.warn("安装助手失败:" + errMsg) } else {
return teaweb.confirm("确定要开始安装此节点吗?", function () {
case "TEST_FAILED": installingNode = node
teaweb.warn("环境测试失败:" + errMsg) that.isInstalling = true
return node.isInstalling = true
case "RPC_TEST_FAILED":
teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败具体错误" + errMsg + "<br/>现在修改API信息", function () { that.$post("$")
window.location = "/api" .params({
}) nodeId: node.id
return })
default: })
teaweb.warn("安装失败:" + errMsg) }
} }
}
} this.installBatch = function () {
} let that = this
}) this.isBatch = true
.done(function () { teaweb.confirm("确定要批量安装选中的节点吗?", function () {
setTimeout(this.reload, 3000) that.installNext()
}) })
} else { }
setTimeout(this.reload, 3000)
} /**
} * 安装下一个
*/
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 + "<br/>现在修改API信息", function () {
window.location = "/api"
})
return
default:
teaweb.warn("安装失败:" + errMsg)
}
}
}
}
})
.done(function () {
setTimeout(this.reload, 3000)
})
} else {
setTimeout(this.reload, 3000)
}
}
}) })

View File

@@ -5,3 +5,13 @@
.right-box { .right-box {
top: 10em; top: 10em;
} }
h3 {
position: relative;
}
h3 button {
position: absolute;
right: 1em;
top: -0.2em;
}

View File

@@ -1,45 +1,103 @@
Tea.context(function () { Tea.context(function () {
this.$delay(function () { let isInstalling = false
this.reloadStatus(this.nodeId)
})
// 开始安装 this.$delay(function () {
this.install = function () { this.reloadStatus(this.nodeId)
this.$post("$") })
.params({
nodeId: this.nodeId
})
.success(function () {
}) // 开始安装
} this.install = function () {
isInstalling = true
// 设置节点安装状态 this.$post("$")
this.updateNodeIsInstalled = function (isInstalled) { .params({
teaweb.confirm("确定要将当前节点修改为未安装状态?", function () { nodeId: this.nodeId
this.$post("/clusters/cluster/node/updateInstallStatus") })
.params({ .success(function () {
nodeId: this.nodeId,
isInstalled: isInstalled ? 1 : 0
})
.refresh()
})
}
// 刷新状态 })
this.reloadStatus = function (nodeId) { }
this.$post("/clusters/cluster/node/status")
.params({ // 设置节点安装状态
nodeId: nodeId this.updateNodeIsInstalled = function (isInstalled) {
}) teaweb.confirm("确定要将当前节点修改为未安装状态?", function () {
.success(function (resp) { this.$post("/clusters/cluster/node/updateInstallStatus")
this.installStatus = resp.data.installStatus .params({
this.node.isInstalled = resp.data.isInstalled nodeId: this.nodeId,
}) isInstalled: isInstalled ? 1 : 0
.done(function () { })
this.$delay(function () { .refresh()
this.reloadStatus(nodeId) })
}, 1000) }
});
} // 刷新状态
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 + "<br/>现在修改API信息", function () {
window.location = "/api"
})
return
default:
shouldReload = true
//teaweb.warn("安装失败:" + errMsg)
}
})
.done(function () {
this.$delay(function () {
this.reloadStatus(nodeId)
}, 1000)
});
}
}) })

View File

@@ -25,6 +25,7 @@
<td> <td>
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/> <input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
<dns-route-selector :v-all-routes="allDNSRoutes" :v-routes="dnsRoutes"></dns-route-selector> <dns-route-selector :v-all-routes="allDNSRoutes" :v-routes="dnsRoutes"></dns-route-selector>
<p class="comment">当前节点对应的DNS线路可用线路是根据集群设置的域名获取的注意DNS服务商可能对这些线路有其他限制。</p>
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@@ -14,7 +14,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td>SSH主机端口 &</td> <td>SSH主机端口 *</td>
<td> <td>
<input type="text" name="sshPort" maxlength="5" v-model="params.port" style="width:6em"/> <input type="text" name="sshPort" maxlength="5" v-model="params.port" style="width:6em"/>
<p class="comment">比如22。</p> <p class="comment">比如22。</p>

View File

@@ -4,4 +4,12 @@
.right-box { .right-box {
top: 10em; top: 10em;
} }
h3 {
position: relative;
}
h3 button {
position: absolute;
right: 1em;
top: -0.2em;
}
/*# sourceMappingURL=upgradeRemote.css.map */ /*# sourceMappingURL=upgradeRemote.css.map */

View File

@@ -1 +1 @@
{"version":3,"sources":["upgradeRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"upgradeRemote.css"} {"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"}

View File

@@ -6,10 +6,15 @@
<p class="comment" v-if="nodes.length == 0">暂时没有需要升级的节点。</p> <p class="comment" v-if="nodes.length == 0">暂时没有需要升级的节点。</p>
<div v-if="nodes.length > 0"> <div v-if="nodes.length > 0">
<h3>所有需要升级的节点</h3> <h3>所有需要升级的节点
<button class="ui button primary tiny" v-if="countCheckedNodes() > 0" @click.prevent="installBatch()">批量安装({{countCheckedNodes()}})</button>
</h3>
<table class="ui table selectable celled"> <table class="ui table selectable celled">
<thead> <thead>
<tr> <tr>
<th style="width:3em">
<checkbox @input="checkNodes"></checkbox>
</th>
<th>节点名</th> <th>节点名</th>
<th>访问IP</th> <th>访问IP</th>
<th>SSH地址</th> <th>SSH地址</th>
@@ -19,6 +24,9 @@
</tr> </tr>
</thead> </thead>
<tr v-for="node in nodes"> <tr v-for="node in nodes">
<td>
<checkbox v-model="node.isChecked" v-if="node.installStatus == null || !node.installStatus.isOk"></checkbox>
</td>
<td> <td>
<link-icon :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">{{node.name}}</link-icon> <link-icon :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">{{node.name}}</link-icon>
</td> </td>

View File

@@ -1,87 +1,151 @@
Tea.context(function () { Tea.context(function () {
this.isInstalling = false this.isInstalling = false
let installingNode = null this.isBatch = false
let installingNode = null
this.$delay(function () { this.nodes.forEach(function (v) {
this.reload() v.isChecked = false
}) })
this.installNode = function (node) { this.$delay(function () {
let that = this this.reload()
teaweb.confirm("确定要开始升级此节点吗?", function () { })
installingNode = node
that.isInstalling = true
node.isInstalling = true
that.$post("$") let that = this
.params({
nodeId: node.id
})
})
}
this.reload = function () { this.checkNodes = function (isChecked) {
let that = this this.nodes.forEach(function (v) {
if (installingNode != null) { v.isChecked = isChecked
this.$post("/clusters/cluster/upgradeStatus") })
.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
switch (resp.data.status.errorCode) { this.countCheckedNodes = function () {
case "EMPTY_LOGIN": return that.nodes.$count(function (k, v) {
case "EMPTY_SSH_HOST": return v.isChecked
case "EMPTY_SSH_PORT": })
case "EMPTY_GRANT": }
teaweb.warn("需要填写SSH登录信息", function () {
teaweb.popup("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, { this.installNode = function (node) {
callback: function () { let that = this
teaweb.reload() if (this.isBatch) {
} installingNode = node
}) that.isInstalling = true
}) node.isInstalling = true
return
case "CREATE_ROOT_DIRECTORY_FAILED": that.$post("$")
teaweb.warn("创建根目录失败,请检查目录权限或者手工创建:" + errMsg) .params({
return nodeId: node.id
case "INSTALL_HELPER_FAILED": })
teaweb.warn("安装助手失败:" + errMsg) } else {
return teaweb.confirm("确定要开始升级此节点吗?", function () {
case "TEST_FAILED": installingNode = node
teaweb.warn("环境测试失败:" + errMsg) that.isInstalling = true
return node.isInstalling = true
case "RPC_TEST_FAILED":
teaweb.confirm("html:要升级的节点到API服务之间的RPC通讯测试失败具体错误" + errMsg + "<br/>现在修改API信息", function () { that.$post("$")
window.location = "/api" .params({
}) nodeId: node.id
return })
default: })
teaweb.warn("升级失败:" + errMsg) }
} }
}
} this.installBatch = function () {
} let that = this
}) this.isBatch = true
.done(function () { teaweb.confirm("确定要批量升级选中的节点吗?", function () {
setTimeout(this.reload, 3000) that.installNext()
}) })
} else { }
setTimeout(this.reload, 3000)
} /**
} * 安装下一个
*/
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/upgradeStatus")
.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 "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 + "<br/>现在修改API信息", function () {
window.location = "/api"
})
return
default:
teaweb.warn("升级失败:" + errMsg)
}
}
}
}
})
.done(function () {
setTimeout(this.reload, 3000)
})
} else {
setTimeout(this.reload, 3000)
}
}
}) })

View File

@@ -5,3 +5,13 @@
.right-box { .right-box {
top: 10em; top: 10em;
} }
h3 {
position: relative;
}
h3 button {
position: absolute;
right: 1em;
top: -0.2em;
}

View File

@@ -17,7 +17,7 @@
<input type="checkbox" name="canAccess" value="1" checked="checked"/> <input type="checkbox" name="canAccess" value="1" checked="checked"/>
<label></label> <label></label>
</div> </div>
<p class="comment">是否为可以公开访问的IP。</p> <p class="comment">是否为可以公开访问的IP如果选中也会作为DNS解析记录的值使用</p>
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@@ -18,7 +18,7 @@
<input type="checkbox" name="canAccess" value="1" v-model="address.canAccess"/> <input type="checkbox" name="canAccess" value="1" v-model="address.canAccess"/>
<label></label> <label></label>
</div> </div>
<p class="comment">是否为可以公开访问的IP。</p> <p class="comment">是否为可以公开访问的IP如果选中也会作为DNS解析记录的值使用</p>
</td> </td>
</tr> </tr>
<tr> <tr>