节点根据健康检查自动上下线

This commit is contained in:
刘祥超
2020-11-15 21:17:52 +08:00
parent e09f1575b4
commit 120aff71a5
17 changed files with 213 additions and 48 deletions

View File

@@ -37,6 +37,33 @@ func (this *CreateNodeAction) RunGet(params struct {
} }
this.Data["leftMenuItems"] = leftMenuItems this.Data["leftMenuItems"] = leftMenuItems
// DNS线路
clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId})
if err != nil {
this.ErrorPage(err)
return
}
dnsRouteMaps := []maps.Map{}
this.Data["dnsDomainId"] = 0
if clusterDNSResp.Domain != nil {
domainId := clusterDNSResp.Domain.Id
this.Data["dnsDomainId"] = domainId
if domainId > 0 {
routesResp, err := this.RPC().DNSDomainRPC().FindAllDNSDomainRoutes(this.AdminContext(), &pb.FindAllDNSDomainRoutesRequest{DnsDomainId: domainId})
if err != nil {
this.ErrorPage(err)
return
}
for _, route := range routesResp.Routes {
dnsRouteMaps = append(dnsRouteMaps, maps.Map{
"name": route.Name,
"code": route.Code,
})
}
}
}
this.Data["dnsRoutes"] = dnsRouteMaps
this.Show() this.Show()
} }
@@ -49,6 +76,9 @@ func (this *CreateNodeAction) RunPost(params struct {
SshHost string SshHost string
SshPort int SshPort int
DnsDomainId int64
DnsRoute string
Must *actions.Must Must *actions.Must
}) { }) {
params.Must. params.Must.
@@ -78,10 +108,12 @@ func (this *CreateNodeAction) RunPost(params struct {
// 保存 // 保存
createResp, err := this.RPC().NodeRPC().CreateNode(this.AdminContext(), &pb.CreateNodeRequest{ createResp, err := this.RPC().NodeRPC().CreateNode(this.AdminContext(), &pb.CreateNodeRequest{
Name: params.Name, Name: params.Name,
ClusterId: params.ClusterId, ClusterId: params.ClusterId,
GroupId: params.GroupId, GroupId: params.GroupId,
Login: loginInfo, Login: loginInfo,
DnsDomainId: params.DnsDomainId,
DnsRoute: params.DnsRoute,
}) })
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)

View File

@@ -102,6 +102,7 @@ func (this *IndexAction) RunGet(params struct {
"name": node.Name, "name": node.Name,
"isInstalled": node.IsInstalled, "isInstalled": node.IsInstalled,
"isOn": node.IsOn, "isOn": node.IsOn,
"isUp": node.IsUp,
"installStatus": maps.Map{ "installStatus": maps.Map{
"isRunning": node.InstallStatus.IsRunning, "isRunning": node.InstallStatus.IsRunning,
"isFinished": node.InstallStatus.IsFinished, "isFinished": node.InstallStatus.IsFinished,

View File

@@ -39,6 +39,8 @@ func (this *IndexAction) RunGet(params struct {
return return
} }
this.Data["dnsName"] = dnsInfoResp.Name this.Data["dnsName"] = dnsInfoResp.Name
this.Data["nodesAutoSync"] = dnsInfoResp.NodesAutoSync
this.Data["serversAutoSync"] = dnsInfoResp.ServersAutoSync
if dnsInfoResp.Domain != nil { if dnsInfoResp.Domain != nil {
this.Data["domainId"] = dnsInfoResp.Domain.Id this.Data["domainId"] = dnsInfoResp.Domain.Id
this.Data["domainName"] = dnsInfoResp.Domain.Name this.Data["domainName"] = dnsInfoResp.Domain.Name
@@ -50,8 +52,10 @@ func (this *IndexAction) RunGet(params struct {
func (this *IndexAction) RunPost(params struct { func (this *IndexAction) RunPost(params struct {
ClusterId int64 ClusterId int64
DnsDomainId int64 DnsDomainId int64
DnsName string DnsName string
NodesAutoSync bool
ServersAutoSync bool
Must *actions.Must Must *actions.Must
CSRF *actionutils.CSRF CSRF *actionutils.CSRF
@@ -80,9 +84,11 @@ func (this *IndexAction) RunPost(params struct {
} }
_, err := this.RPC().NodeClusterRPC().UpdateNodeClusterDNS(this.AdminContext(), &pb.UpdateNodeClusterDNSRequest{ _, err := this.RPC().NodeClusterRPC().UpdateNodeClusterDNS(this.AdminContext(), &pb.UpdateNodeClusterDNSRequest{
NodeClusterId: params.ClusterId, NodeClusterId: params.ClusterId,
DnsName: params.DnsName, DnsName: params.DnsName,
DnsDomainId: params.DnsDomainId, DnsDomainId: params.DnsDomainId,
NodesAutoSync: params.NodesAutoSync,
ServersAutoSync: params.ServersAutoSync,
}) })
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)

View File

@@ -51,9 +51,9 @@ func (this *ClusterHelper) BeforeAction(action *actions.ActionObject) {
tabbar := actionutils.NewTabbar() tabbar := actionutils.NewTabbar()
tabbar.Add("集群列表", "", "/clusters", "", false) tabbar.Add("集群列表", "", "/clusters", "", false)
tabbar.Add("节点", "", "/clusters/cluster?clusterId="+clusterIdString, "server", selectedTabbar == "node") tabbar.Add("集群节点", "", "/clusters/cluster?clusterId="+clusterIdString, "server", selectedTabbar == "node")
tabbar.Add("设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting") tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
tabbar.Add("删除", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete") tabbar.Add("删除集群", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
{ {
m := tabbar.Add("当前集群:"+cluster.Name, "", "/clusters/cluster?clusterId="+clusterIdString, "", false) m := tabbar.Add("当前集群:"+cluster.Name, "", "/clusters/cluster?clusterId="+clusterIdString, "", false)

View File

@@ -29,6 +29,8 @@ func (this *UpdateClusterPopupAction) RunGet(params struct {
return return
} }
this.Data["dnsName"] = dnsResp.Name this.Data["dnsName"] = dnsResp.Name
this.Data["nodesAutoSync"] = dnsResp.NodesAutoSync
this.Data["serversAutoSync"] = dnsResp.ServersAutoSync
if dnsResp.Domain != nil { if dnsResp.Domain != nil {
this.Data["domainId"] = dnsResp.Domain.Id this.Data["domainId"] = dnsResp.Domain.Id
this.Data["domain"] = dnsResp.Domain.Name this.Data["domain"] = dnsResp.Domain.Name
@@ -63,9 +65,11 @@ func (this *UpdateClusterPopupAction) RunGet(params struct {
} }
func (this *UpdateClusterPopupAction) RunPost(params struct { func (this *UpdateClusterPopupAction) RunPost(params struct {
ClusterId int64 ClusterId int64
DnsName string DnsName string
DomainId int64 DomainId int64
NodesAutoSync bool
ServersAutoSync bool
Must *actions.Must Must *actions.Must
CSRF *actionutils.CSRF CSRF *actionutils.CSRF
@@ -94,9 +98,11 @@ func (this *UpdateClusterPopupAction) RunPost(params struct {
} }
_, err = this.RPC().NodeClusterRPC().UpdateNodeClusterDNS(this.AdminContext(), &pb.UpdateNodeClusterDNSRequest{ _, err = this.RPC().NodeClusterRPC().UpdateNodeClusterDNS(this.AdminContext(), &pb.UpdateNodeClusterDNSRequest{
NodeClusterId: params.ClusterId, NodeClusterId: params.ClusterId,
DnsName: params.DnsName, DnsName: params.DnsName,
DnsDomainId: params.DomainId, DnsDomainId: params.DomainId,
NodesAutoSync: params.NodesAutoSync,
ServersAutoSync: params.ServersAutoSync,
}) })
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)

View File

@@ -13,7 +13,7 @@ type DeleteAction struct {
func (this *DeleteAction) RunPost(params struct { func (this *DeleteAction) RunPost(params struct {
NodeId int64 NodeId int64
}) { }) {
_, err := this.RPC().NodeRPC().DisableNode(this.AdminContext(), &pb.DisableNodeRequest{NodeId: params.NodeId}) _, err := this.RPC().NodeRPC().DeleteNode(this.AdminContext(), &pb.DeleteNodeRequest{NodeId: params.NodeId})
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)
return return

View File

@@ -14,7 +14,10 @@ Vue.component("health-check-config-box", {
statusCodes: [200], statusCodes: [200],
timeout: {count: 10, unit: "second"}, timeout: {count: 10, unit: "second"},
countTries: 3, countTries: 3,
tryDelay: {count: 100, unit: "ms"} tryDelay: {count: 100, unit: "ms"},
autoDown: true,
countUp: 1,
countDown: 1
} }
let that = this let that = this
setTimeout(function () { setTimeout(function () {
@@ -44,7 +47,14 @@ Vue.component("health-check-config-box", {
if (healthCheckConfig.tryDelay == null) { if (healthCheckConfig.tryDelay == null) {
healthCheckConfig.tryDelay = {count: 100, unit: "ms"} healthCheckConfig.tryDelay = {count: 100, unit: "ms"}
} }
if (healthCheckConfig.countUp == null || healthCheckConfig.countUp < 1) {
healthCheckConfig.countUp = 1
}
if (healthCheckConfig.countDown == null || healthCheckConfig.countDown < 1) {
healthCheckConfig.countDown = 1
}
} }
console.log(healthCheckConfig.countUp, healthCheckConfig.countDown)
return { return {
healthCheck: healthCheckConfig, healthCheck: healthCheckConfig,
advancedVisible: false, advancedVisible: false,
@@ -79,6 +89,22 @@ Vue.component("health-check-config-box", {
} else { } else {
this.healthCheck.countTries = 0 this.healthCheck.countTries = 0
} }
},
"healthCheck.countUp": function (v) {
let count = parseInt(v)
if (!isNaN(count)) {
this.healthCheck.countUp = count
} else {
this.healthCheck.countUp = 0
}
},
"healthCheck.countDown": function (v) {
let count = parseInt(v)
if (!isNaN(count)) {
this.healthCheck.countDown = count
} else {
this.healthCheck.countDown = 0
}
} }
}, },
methods: { methods: {
@@ -145,6 +171,30 @@ Vue.component("health-check-config-box", {
<time-duration-box :v-value="healthCheck.interval"></time-duration-box> <time-duration-box :v-value="healthCheck.interval"></time-duration-box>
</td> </td>
</tr> </tr>
<tr>
<td>是否自动下线</td>
<td>
<div class="ui checkbox">
<input type="checkbox" value="1" v-model="healthCheck.autoDown"/>
<label></label>
</div>
<p class="comment">选中后系统会根据健康检查的结果自动标记节点的上线/下线状态。</p>
</td>
</tr>
<tr v-show="healthCheck.autoDown">
<td>连续上线次数</td>
<td>
<input type="text" v-model="healthCheck.countUp" style="width:5em" maxlength="6"/>
<p class="comment">连续N次检查成功后自动恢复上线。</p>
</td>
</tr>
<tr v-show="healthCheck.autoDown">
<td>连续下线次数</td>
<td>
<input type="text" v-model="healthCheck.countDown" style="width:5em" maxlength="6"/>
<p class="comment">连续N次检查失败后自动下线。</p>
</td>
</tr>
</tbody> </tbody>
<tbody v-show="healthCheck.isOn"> <tbody v-show="healthCheck.isOn">
<tr> <tr>
@@ -176,7 +226,7 @@ Vue.component("health-check-config-box", {
<time-duration-box :v-value="healthCheck.tryDelay"></time-duration-box> <time-duration-box :v-value="healthCheck.tryDelay"></time-duration-box>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div class="margin"></div> <div class="margin"></div>
</div>` </div>`

View File

@@ -31,9 +31,15 @@ Vue.component("message-row", {
<td> <td>
{{message.body}} {{message.body}}
<div v-if="message.type == 'HealthCheckFail'" style="margin-top: 0.8em"> <!-- 健康检查 -->
<div v-if="message.type == 'HealthCheckFailed'" style="margin-top: 0.8em">
<a :href="'/clusters/cluster/node?clusterId=' + message.cluster.id + '&nodeId=' + param.node.id" v-for="param in params" class="ui label tiny" style="margin-bottom: 0.5em">{{param.node.name}}: {{param.error}}</a> <a :href="'/clusters/cluster/node?clusterId=' + message.cluster.id + '&nodeId=' + param.node.id" v-for="param in params" class="ui label tiny" style="margin-bottom: 0.5em">{{param.node.name}}: {{param.error}}</a>
</div> </div>
<!-- 集群DNS设置 -->
<div v-if="message.type == 'ClusterDNSSyncFailed'" style="margin-top: 0.8em">
<a :href="'/dns/clusters/cluster?clusterId=' + message.cluster.id">查看问题 &raquo;</a>
</div>
</td> </td>
</tr> </tr>
</table> </table>

View File

@@ -16,6 +16,18 @@
<td>IP地址</td> <td>IP地址</td>
<td> <td>
<node-ip-addresses-box></node-ip-addresses-box> <node-ip-addresses-box></node-ip-addresses-box>
<p class="comment">用于访问节点和域名解析等。</p>
</td>
</tr>
<tr v-if="dnsRoutes.length > 0">
<td>DNS线路</td>
<td>
<input type="hidden" name="dnsDomainId" :value="dnsDomainId"/>
<select class="ui dropdown auto-width" name="dnsRoute">
<option value="">[请选择]</option>
<option v-for="route in dnsRoutes" :value="route.code">{{route.name}}</option>
</select>
<p class="comment">可用线路是根据集群设置的域名获取的注意DNS服务商可能对这些线路有所限制。</p>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -25,29 +37,31 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td> <td colspan="2">
<more-options-indicator></more-options-indicator>
</td>
</tr> </tr>
<tbody v-show="moreOptionsVisible"> <tbody v-show="moreOptionsVisible">
<tr> <tr>
<td>SSH主机地址</td> <td>SSH主机地址</td>
<td> <td>
<input type="text" name="sshHost" maxlength="64"/> <input type="text" name="sshHost" maxlength="64"/>
<p class="comment">比如192.168.1.100</p> <p class="comment">比如192.168.1.100</p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>SSH主机端口</td> <td>SSH主机端口</td>
<td> <td>
<input type="text" name="sshPort" maxlength="5"/> <input type="text" name="sshPort" maxlength="5"/>
<p class="comment">常见的比如22。</p> <p class="comment">常见的比如22。</p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>SSH登录认证</td> <td>SSH登录认证</td>
<td> <td>
<grant-selector></grant-selector> <grant-selector></grant-selector>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<submit-btn></submit-btn> <submit-btn></submit-btn>

View File

@@ -87,7 +87,10 @@
<span v-else class="disabled">-</span> <span v-else class="disabled">-</span>
</td> </td>
<td> <td>
<div v-if="!node.isOn"> <div v-if="!node.isUp">
<span class="red">健康问题</span>
</div>
<div v-else-if="!node.isOn">
<label-on :v-is-on="node.isOn"></label-on> <label-on :v-is-on="node.isOn"></label-on>
</div> </div>
<div v-else-if="node.isInstalled"> <div v-else-if="node.isInstalled">

View File

@@ -17,6 +17,7 @@
<td>IP地址</td> <td>IP地址</td>
<td> <td>
<node-ip-addresses-box :v-ip-addresses="ipAddresses"></node-ip-addresses-box> <node-ip-addresses-box :v-ip-addresses="ipAddresses"></node-ip-addresses-box>
<p class="comment">用于访问节点和域名解析等。</p>
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@@ -25,6 +25,29 @@
<p class="comment">和主域名一起组成子域名。</p> <p class="comment">和主域名一起组成子域名。</p>
</td> </td>
</tr> </tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>是否同步节点DNS状态</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="nodesAutoSync" value="1" v-model="nodesAutoSync"/>
<label></label>
</div>
</td>
</tr>
<tr>
<td>是否同步网站服务DNS状态</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="serversAutoSync" value="1" v-model="serversAutoSync"/>
<label></label>
</div>
</td>
</tr>
</tbody>
</table> </table>
<submit-btn></submit-btn> <submit-btn></submit-btn>

View File

@@ -9,7 +9,7 @@
<table class="ui table definition selectable"> <table class="ui table definition selectable">
<tr> <tr>
<td class="title">集群</td> <td class="title">集群</td>
<td>{{cluster.name}}</td> <td><link-icon :href="'/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</link-icon></td>
</tr> </tr>
<tr> <tr>
<td>子域名</td> <td>子域名</td>

View File

@@ -1,7 +1,7 @@
Tea.context(function () { Tea.context(function () {
this.updateCluster = function (clusterId) { this.updateCluster = function (clusterId) {
teaweb.popup("/dns/updateClusterPopup?clusterId=" + clusterId, { teaweb.popup("/dns/updateClusterPopup?clusterId=" + clusterId, {
height: "22em", height: "25em",
callback: function () { callback: function () {
teaweb.success("保存成功", function () { teaweb.success("保存成功", function () {
teaweb.reload() teaweb.reload()

View File

@@ -1,7 +1,7 @@
Tea.context(function () { Tea.context(function () {
this.updateCluster = function (clusterId) { this.updateCluster = function (clusterId) {
teaweb.popup("/dns/updateClusterPopup?clusterId=" + clusterId, { teaweb.popup("/dns/updateClusterPopup?clusterId=" + clusterId, {
height: "22em", height: "25em",
callback: function () { callback: function () {
teaweb.success("保存成功", function () { teaweb.success("保存成功", function () {
teaweb.reload() teaweb.reload()

View File

@@ -8,7 +8,7 @@ Tea.context(function () {
this.updateCluster = function (clusterId) { this.updateCluster = function (clusterId) {
let that = this let that = this
teaweb.popup("/dns/updateClusterPopup?clusterId=" + clusterId, { teaweb.popup("/dns/updateClusterPopup?clusterId=" + clusterId, {
height: "22em", height: "25em",
callback: function () { callback: function () {
teaweb.success("保存成功", function () { teaweb.success("保存成功", function () {
that.reload() that.reload()

View File

@@ -43,6 +43,29 @@
<p class="comment">子域名和主域名共同组成集群的域名。</p> <p class="comment">子域名和主域名共同组成集群的域名。</p>
</td> </td>
</tr> </tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>是否同步节点DNS状态</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="nodesAutoSync" value="1" v-model="nodesAutoSync"/>
<label></label>
</div>
</td>
</tr>
<tr>
<td>是否同步网站服务DNS状态</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="serversAutoSync" value="1" v-model="serversAutoSync"/>
<label></label>
</div>
</td>
</tr>
</tbody>
</table> </table>
<submit-btn></submit-btn> <submit-btn></submit-btn>