mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-07 15:20:25 +08:00
可以在集群中查看待安装节点、并直接安装节点
This commit is contained in:
11
internal/utils/numberutils/utils.go
Normal file
11
internal/utils/numberutils/utils.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package numberutils
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func FormatInt64(value int64) string {
|
||||||
|
return strconv.FormatInt(value, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatInt(value int) string {
|
||||||
|
return strconv.Itoa(value)
|
||||||
|
}
|
||||||
98
internal/web/actions/default/clusters/cluster/createBatch.go
Normal file
98
internal/web/actions/default/clusters/cluster/createBatch.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateBatchAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreateBatchAction) Init() {
|
||||||
|
this.Nav("", "node", "create")
|
||||||
|
this.SecondMenu("nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreateBatchAction) RunGet(params struct {
|
||||||
|
ClusterId int64
|
||||||
|
}) {
|
||||||
|
leftMenuItems := []maps.Map{
|
||||||
|
{
|
||||||
|
"name": "单个创建",
|
||||||
|
"url": "/clusters/cluster/createNode?clusterId=" + strconv.FormatInt(params.ClusterId, 10),
|
||||||
|
"isActive": false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "批量创建",
|
||||||
|
"url": "/clusters/cluster/createBatch?clusterId=" + strconv.FormatInt(params.ClusterId, 10),
|
||||||
|
"isActive": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
this.Data["leftMenuItems"] = leftMenuItems
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreateBatchAction) RunPost(params struct {
|
||||||
|
ClusterId int64
|
||||||
|
IpList string
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
if params.ClusterId <= 0 {
|
||||||
|
this.Fail("请选择正确的集群")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验
|
||||||
|
// TODO 支持IP范围,比如:192.168.1.[100-105]
|
||||||
|
realIPList := []string{}
|
||||||
|
for _, ip := range strings.Split(params.IpList, "\n") {
|
||||||
|
ip = strings.TrimSpace(ip)
|
||||||
|
if len(ip) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ip = strings.ReplaceAll(ip, " ", "")
|
||||||
|
|
||||||
|
if net.ParseIP(ip) == nil {
|
||||||
|
this.Fail("发现错误的IP地址:" + ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lists.ContainsString(realIPList, ip) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
realIPList = append(realIPList, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
for _, ip := range realIPList {
|
||||||
|
resp, err := this.RPC().NodeRPC().CreateNode(this.AdminContext(), &pb.CreateNodeRequest{
|
||||||
|
Name: ip,
|
||||||
|
ClusterId: params.ClusterId,
|
||||||
|
Login: nil,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeId := resp.NodeId
|
||||||
|
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
|
||||||
|
NodeId: nodeId,
|
||||||
|
Name: "IP地址",
|
||||||
|
Ip: ip,
|
||||||
|
CanAccess: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
120
internal/web/actions/default/clusters/cluster/createNode.go
Normal file
120
internal/web/actions/default/clusters/cluster/createNode.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 创建节点
|
||||||
|
type CreateNodeAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreateNodeAction) Init() {
|
||||||
|
this.Nav("", "node", "create")
|
||||||
|
this.SecondMenu("nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreateNodeAction) RunGet(params struct {
|
||||||
|
ClusterId int64
|
||||||
|
}) {
|
||||||
|
leftMenuItems := []maps.Map{
|
||||||
|
{
|
||||||
|
"name": "单个创建",
|
||||||
|
"url": "/clusters/cluster/createNode?clusterId=" + strconv.FormatInt(params.ClusterId, 10),
|
||||||
|
"isActive": true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "批量创建",
|
||||||
|
"url": "/clusters/cluster/createBatch?clusterId=" + strconv.FormatInt(params.ClusterId, 10),
|
||||||
|
"isActive": false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
this.Data["leftMenuItems"] = leftMenuItems
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreateNodeAction) RunPost(params struct {
|
||||||
|
Name string
|
||||||
|
IpAddressesJSON []byte
|
||||||
|
ClusterId int64
|
||||||
|
GrantId int64
|
||||||
|
SshHost string
|
||||||
|
SshPort int
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
params.Must.
|
||||||
|
Field("name", params.Name).
|
||||||
|
Require("请输入节点名称")
|
||||||
|
|
||||||
|
if len(params.IpAddressesJSON) == 0 {
|
||||||
|
this.Fail("请至少添加一个IP地址")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 检查cluster
|
||||||
|
if params.ClusterId <= 0 {
|
||||||
|
this.Fail("请选择所在集群")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 检查登录授权
|
||||||
|
loginInfo := &pb.NodeLogin{
|
||||||
|
Id: 0,
|
||||||
|
Name: "SSH",
|
||||||
|
Type: "ssh",
|
||||||
|
Params: maps.Map{
|
||||||
|
"grantId": params.GrantId,
|
||||||
|
"host": params.SshHost,
|
||||||
|
"port": params.SshPort,
|
||||||
|
}.AsJSON(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
createResp, err := this.RPC().NodeRPC().CreateNode(this.AdminContext(), &pb.CreateNodeRequest{
|
||||||
|
Name: params.Name,
|
||||||
|
ClusterId: params.ClusterId,
|
||||||
|
Login: loginInfo,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeId := createResp.NodeId
|
||||||
|
|
||||||
|
// IP地址
|
||||||
|
ipAddresses := []maps.Map{}
|
||||||
|
if len(params.IpAddressesJSON) > 0 {
|
||||||
|
err = json.Unmarshal(params.IpAddressesJSON, &ipAddresses)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, address := range ipAddresses {
|
||||||
|
addressId := address.GetInt64("id")
|
||||||
|
if addressId > 0 {
|
||||||
|
_, err = this.RPC().NodeIPAddressRPC().UpdateNodeIPAddressNodeId(this.AdminContext(), &pb.UpdateNodeIPAddressNodeIdRequest{
|
||||||
|
AddressId: addressId,
|
||||||
|
NodeId: nodeId,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
|
||||||
|
NodeId: nodeId,
|
||||||
|
Name: address.GetString("name"),
|
||||||
|
Ip: address.GetString("ip"),
|
||||||
|
CanAccess: address.GetBool("canAccess"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -15,11 +15,15 @@ func init() {
|
|||||||
Prefix("/clusters/cluster").
|
Prefix("/clusters/cluster").
|
||||||
Get("", new(IndexAction)).
|
Get("", new(IndexAction)).
|
||||||
GetPost("/installNodes", new(InstallNodesAction)).
|
GetPost("/installNodes", new(InstallNodesAction)).
|
||||||
|
GetPost("/installRemote", new(InstallRemoteAction)).
|
||||||
|
Post("/installStatus", new(InstallStatusAction)).
|
||||||
GetPost("/delete", new(DeleteAction)).
|
GetPost("/delete", new(DeleteAction)).
|
||||||
|
GetPost("/createNode", new(CreateNodeAction)).
|
||||||
|
GetPost("/createBatch", new(CreateBatchAction)).
|
||||||
|
GetPost("/updateNodeSSH", new(UpdateNodeSSHAction)).
|
||||||
|
|
||||||
// 节点相关
|
// 节点相关
|
||||||
Get("/node", new(node.NodeAction)).
|
Get("/node", new(node.NodeAction)).
|
||||||
GetPost("/node/create", new(node.CreateAction)).
|
|
||||||
GetPost("/node/update", new(node.UpdateAction)).
|
GetPost("/node/update", new(node.UpdateAction)).
|
||||||
GetPost("/node/install", new(node.InstallAction)).
|
GetPost("/node/install", new(node.InstallAction)).
|
||||||
Post("/node/updateInstallStatus", new(node.UpdateInstallStatusAction)).
|
Post("/node/updateInstallStatus", new(node.UpdateInstallStatusAction)).
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cluster
|
package cluster
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||||
"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/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -19,6 +20,19 @@ func (this *InstallNodesAction) Init() {
|
|||||||
func (this *InstallNodesAction) RunGet(params struct {
|
func (this *InstallNodesAction) RunGet(params struct {
|
||||||
ClusterId int64
|
ClusterId int64
|
||||||
}) {
|
}) {
|
||||||
|
this.Data["leftMenuItems"] = []maps.Map{
|
||||||
|
{
|
||||||
|
"name": "自动注册",
|
||||||
|
"url": "/clusters/cluster/installNodes?clusterId=" + numberutils.FormatInt64(params.ClusterId),
|
||||||
|
"isActive": true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "远程安装",
|
||||||
|
"url": "/clusters/cluster/installRemote?clusterId=" + numberutils.FormatInt64(params.ClusterId),
|
||||||
|
"isActive": false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{ClusterId: params.ClusterId})
|
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{ClusterId: params.ClusterId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InstallRemoteAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *InstallRemoteAction) Init() {
|
||||||
|
this.Nav("", "node", "install")
|
||||||
|
this.SecondMenu("nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *InstallRemoteAction) RunGet(params struct {
|
||||||
|
ClusterId int64
|
||||||
|
}) {
|
||||||
|
this.Data["leftMenuItems"] = []maps.Map{
|
||||||
|
{
|
||||||
|
"name": "自动注册",
|
||||||
|
"url": "/clusters/cluster/installNodes?clusterId=" + numberutils.FormatInt64(params.ClusterId),
|
||||||
|
"isActive": false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "远程安装",
|
||||||
|
"url": "/clusters/cluster/installRemote?clusterId=" + numberutils.FormatInt64(params.ClusterId),
|
||||||
|
"isActive": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
nodesResp, err := this.RPC().NodeRPC().FindAllNotInstalledNodesWithClusterId(this.AdminContext(), &pb.FindAllNotInstalledNodesWithClusterIdRequest{ClusterId: params.ClusterId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeMaps := []maps.Map{}
|
||||||
|
for _, node := range nodesResp.Nodes {
|
||||||
|
loginParams := maps.Map{}
|
||||||
|
if node.Login != nil && len(node.Login.Params) > 0 {
|
||||||
|
err := json.Unmarshal(node.Login.Params, &loginParams)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
installStatus := maps.Map{
|
||||||
|
"isRunning": false,
|
||||||
|
"isFinished": false,
|
||||||
|
}
|
||||||
|
if node.InstallStatus != nil {
|
||||||
|
installStatus = maps.Map{
|
||||||
|
"isRunning": node.InstallStatus.IsRunning,
|
||||||
|
"isFinished": node.InstallStatus.IsFinished,
|
||||||
|
"isOk": node.InstallStatus.IsOk,
|
||||||
|
"error": node.InstallStatus.Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeMaps = append(nodeMaps, maps.Map{
|
||||||
|
"id": node.Id,
|
||||||
|
"isOn": node.IsOn,
|
||||||
|
"name": node.Name,
|
||||||
|
"addresses": node.IpAddresses,
|
||||||
|
"login": node.Login,
|
||||||
|
"loginParams": loginParams,
|
||||||
|
"installStatus": installStatus,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["nodes"] = nodeMaps
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *InstallRemoteAction) RunPost(params struct {
|
||||||
|
NodeId int64
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
_, err := this.RPC().NodeRPC().InstallNode(this.AdminContext(), &pb.InstallNodeRequest{NodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InstallStatusAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *InstallStatusAction) RunPost(params struct {
|
||||||
|
NodeId int64
|
||||||
|
}) {
|
||||||
|
resp, err := this.RPC().NodeRPC().FindNodeInstallStatus(this.AdminContext(), &pb.FindNodeInstallStatusRequest{NodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.InstallStatus == nil {
|
||||||
|
this.Data["status"] = nil
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["status"] = maps.Map{
|
||||||
|
"isRunning": resp.InstallStatus.IsRunning,
|
||||||
|
"isFinished": resp.InstallStatus.IsFinished,
|
||||||
|
"isOk": resp.InstallStatus.IsOk,
|
||||||
|
"error": resp.InstallStatus.Error,
|
||||||
|
"errorCode": resp.InstallStatus.ErrorCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package node
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
||||||
"github.com/iwind/TeaGo/actions"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 创建节点
|
|
||||||
type CreateAction struct {
|
|
||||||
actionutils.ParentAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CreateAction) Init() {
|
|
||||||
this.Nav("", "node", "create")
|
|
||||||
this.SecondMenu("nodes")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CreateAction) RunGet(params struct{}) {
|
|
||||||
this.Show()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CreateAction) RunPost(params struct {
|
|
||||||
Name string
|
|
||||||
IPAddresses string `alias:"ipAddresses"`
|
|
||||||
ClusterId int64
|
|
||||||
GrantId int64
|
|
||||||
SshHost string
|
|
||||||
SshPort int
|
|
||||||
|
|
||||||
Must *actions.Must
|
|
||||||
}) {
|
|
||||||
params.Must.
|
|
||||||
Field("name", params.Name).
|
|
||||||
Require("请输入节点名称")
|
|
||||||
|
|
||||||
// TODO 检查cluster
|
|
||||||
if params.ClusterId <= 0 {
|
|
||||||
this.Fail("请选择所在集群")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO 检查登录授权
|
|
||||||
loginInfo := &pb.NodeLogin{
|
|
||||||
Id: 0,
|
|
||||||
Name: "SSH",
|
|
||||||
Type: "ssh",
|
|
||||||
Params: maps.Map{
|
|
||||||
"grantId": params.GrantId,
|
|
||||||
"host": params.SshHost,
|
|
||||||
"port": params.SshPort,
|
|
||||||
}.AsJSON(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存
|
|
||||||
createResp, err := this.RPC().NodeRPC().CreateNode(this.AdminContext(), &pb.CreateNodeRequest{
|
|
||||||
Name: params.Name,
|
|
||||||
ClusterId: params.ClusterId,
|
|
||||||
Login: loginInfo,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nodeId := createResp.NodeId
|
|
||||||
|
|
||||||
// IP地址
|
|
||||||
ipAddresses := []maps.Map{}
|
|
||||||
err = json.Unmarshal([]byte(params.IPAddresses), &ipAddresses)
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, address := range ipAddresses {
|
|
||||||
addressId := address.GetInt64("id")
|
|
||||||
_, err = this.RPC().NodeIPAddressRPC().UpdateNodeIPAddressNodeId(this.AdminContext(), &pb.UpdateNodeIPAddressNodeIdRequest{
|
|
||||||
AddressId: addressId,
|
|
||||||
NodeId: nodeId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
this.ErrorPage(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Success()
|
|
||||||
}
|
|
||||||
@@ -76,6 +76,9 @@ func (this *InstallAction) RunGet(params struct {
|
|||||||
apiNodes := apiNodesResp.Nodes
|
apiNodes := apiNodesResp.Nodes
|
||||||
apiEndpoints := []string{}
|
apiEndpoints := []string{}
|
||||||
for _, apiNode := range apiNodes {
|
for _, apiNode := range apiNodes {
|
||||||
|
if !apiNode.IsOn {
|
||||||
|
continue
|
||||||
|
}
|
||||||
apiEndpoints = append(apiEndpoints, apiNode.AccessAddrs...)
|
apiEndpoints = append(apiEndpoints, apiNode.AccessAddrs...)
|
||||||
}
|
}
|
||||||
this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\""
|
this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\""
|
||||||
|
|||||||
119
internal/web/actions/default/clusters/cluster/updateNodeSSH.go
Normal file
119
internal/web/actions/default/clusters/cluster/updateNodeSSH.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateNodeSSHAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateNodeSSHAction) Init() {
|
||||||
|
this.Nav("", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateNodeSSHAction) RunGet(params struct {
|
||||||
|
NodeId int64
|
||||||
|
}) {
|
||||||
|
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if nodeResp.Node == nil {
|
||||||
|
this.NotFound("node", params.NodeId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node := nodeResp.Node
|
||||||
|
this.Data["node"] = maps.Map{
|
||||||
|
"id": node.Id,
|
||||||
|
"name": node.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSH
|
||||||
|
loginParams := maps.Map{
|
||||||
|
"host": "",
|
||||||
|
"port": "",
|
||||||
|
"grantId": 0,
|
||||||
|
}
|
||||||
|
this.Data["loginId"] = 0
|
||||||
|
if node.Login != nil {
|
||||||
|
this.Data["loginId"] = node.Login.Id
|
||||||
|
if len(node.Login.Params) > 0 {
|
||||||
|
err = json.Unmarshal(node.Login.Params, &loginParams)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Data["params"] = loginParams
|
||||||
|
|
||||||
|
// 认证信息
|
||||||
|
grantId := loginParams.GetInt64("grantId")
|
||||||
|
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledGrant(this.AdminContext(), &pb.FindEnabledGrantRequest{GrantId: grantId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
}
|
||||||
|
var grantMap maps.Map = nil
|
||||||
|
if grantResp.Grant != nil {
|
||||||
|
grantMap = maps.Map{
|
||||||
|
"id": grantResp.Grant.Id,
|
||||||
|
"name": grantResp.Grant.Name,
|
||||||
|
"method": grantResp.Grant.Method,
|
||||||
|
"methodName": grantutils.FindGrantMethodName(grantResp.Grant.Method),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Data["grant"] = grantMap
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateNodeSSHAction) RunPost(params struct {
|
||||||
|
NodeId int64
|
||||||
|
LoginId int64
|
||||||
|
SshHost string
|
||||||
|
SshPort int
|
||||||
|
GrantId int64
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
params.Must.
|
||||||
|
Field("sshHost", params.SshHost).
|
||||||
|
Require("请输入SSH主机地址").
|
||||||
|
Field("sshPort", params.SshPort).
|
||||||
|
Gt(0, "SSH主机端口需要大于0").
|
||||||
|
Lt(65535, "SSH主机端口需要小于65535")
|
||||||
|
|
||||||
|
if params.GrantId <= 0 {
|
||||||
|
this.Fail("需要选择或填写至少一个认证信息")
|
||||||
|
}
|
||||||
|
|
||||||
|
login := &pb.NodeLogin{
|
||||||
|
Id: params.LoginId,
|
||||||
|
Name: "SSH",
|
||||||
|
Type: "ssh",
|
||||||
|
Params: maps.Map{
|
||||||
|
"grantId": params.GrantId,
|
||||||
|
"host": params.SshHost,
|
||||||
|
"port": params.SshPort,
|
||||||
|
}.AsJSON(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := this.RPC().NodeRPC().UpdateNodeLogin(this.AdminContext(), &pb.UpdateNodeLoginRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
Login: login,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -36,47 +36,37 @@ func (this *ClusterHelper) BeforeAction(action *actions.ActionObject) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterResp, err := rpcClient.NodeClusterRPC().FindEnabledNodeCluster(rpcClient.Context(action.Context.GetInt64("adminId")), &pb.FindEnabledNodeClusterRequest{ClusterId: clusterId})
|
if clusterId > 0 {
|
||||||
if err != nil {
|
clusterResp, err := rpcClient.NodeClusterRPC().FindEnabledNodeCluster(rpcClient.Context(action.Context.GetInt64("adminId")), &pb.FindEnabledNodeClusterRequest{ClusterId: clusterId})
|
||||||
logs.Error(err)
|
if err != nil {
|
||||||
return
|
logs.Error(err)
|
||||||
}
|
return
|
||||||
cluster := clusterResp.Cluster
|
}
|
||||||
if cluster == nil {
|
cluster := clusterResp.Cluster
|
||||||
action.WriteString("can not find cluster")
|
if cluster == nil {
|
||||||
return
|
action.WriteString("can not find cluster")
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
m["right"] = true
|
m["right"] = true
|
||||||
|
}
|
||||||
|
actionutils.SetTabbar(action, tabbar)
|
||||||
|
|
||||||
|
// 左侧菜单
|
||||||
|
secondMenuItem := action.Data.GetString("secondMenuItem")
|
||||||
|
switch selectedTabbar {
|
||||||
|
case "setting":
|
||||||
|
action.Data["leftMenuItems"] = this.createSettingMenu(clusterIdString, secondMenuItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
actionutils.SetTabbar(action, tabbar)
|
|
||||||
|
|
||||||
// 左侧菜单
|
|
||||||
secondMenuItem := action.Data.GetString("secondMenuItem")
|
|
||||||
switch selectedTabbar {
|
|
||||||
case "setting":
|
|
||||||
action.Data["leftMenuItems"] = this.createSettingMenu(clusterIdString, secondMenuItem)
|
|
||||||
case "node":
|
|
||||||
action.Data["leftMenuItems"] = this.createNodeMenu(clusterIdString, secondMenuItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 节点菜单
|
|
||||||
func (this *ClusterHelper) createNodeMenu(clusterId string, selectedItem string) (items []maps.Map) {
|
|
||||||
items = append(items, maps.Map{
|
|
||||||
"name": "节点列表",
|
|
||||||
"url": "/clusters/cluster?clusterId=" + clusterId,
|
|
||||||
"isActive": selectedItem == "nodes",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置菜单
|
// 设置菜单
|
||||||
|
|||||||
@@ -21,12 +21,28 @@ Vue.component("health-check-config-box", {
|
|||||||
that.changeURL()
|
that.changeURL()
|
||||||
}, 500)
|
}, 500)
|
||||||
} else {
|
} else {
|
||||||
let url = new URL(healthCheckConfig.url)
|
try {
|
||||||
urlProtocol = url.protocol.substring(0, url.protocol.length - 1)
|
let url = new URL(healthCheckConfig.url)
|
||||||
urlPort = url.port
|
urlProtocol = url.protocol.substring(0, url.protocol.length - 1)
|
||||||
urlRequestURI = url.pathname
|
urlPort = url.port
|
||||||
if (url.search.length > 0) {
|
urlRequestURI = url.pathname
|
||||||
urlRequestURI += url.search
|
if (url.search.length > 0) {
|
||||||
|
urlRequestURI += url.search
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (healthCheckConfig.statusCodes == null) {
|
||||||
|
healthCheckConfig.statusCodes = [200]
|
||||||
|
}
|
||||||
|
if (healthCheckConfig.interval == null) {
|
||||||
|
healthCheckConfig.interval = {count: 60, unit: "second"}
|
||||||
|
}
|
||||||
|
if (healthCheckConfig.timeout == null) {
|
||||||
|
healthCheckConfig.timeout = {count: 10, unit: "second"}
|
||||||
|
}
|
||||||
|
if (healthCheckConfig.tryDelay == null) {
|
||||||
|
healthCheckConfig.tryDelay = {count: 100, unit: "ms"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -81,7 +97,6 @@ Vue.component("health-check-config-box", {
|
|||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
console.log(this.healthCheck.statusCodes)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `<div>
|
template: `<div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Vue.component("values-box", {
|
|||||||
props: ["values", "size", "maxlength", "name"],
|
props: ["values", "size", "maxlength", "name"],
|
||||||
data: function () {
|
data: function () {
|
||||||
let values = this.values;
|
let values = this.values;
|
||||||
if (typeof (values) != "object") {
|
if (values == null) {
|
||||||
values = [];
|
values = [];
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<second-menu>
|
<second-menu>
|
||||||
<menu-item :href="'/clusters/cluster?clusterId=' + clusterId" code="index">节点列表</menu-item>
|
<menu-item :href="'/clusters/cluster?clusterId=' + clusterId" code="index">节点列表</menu-item>
|
||||||
<menu-item :href="'/clusters/cluster/node/create?clusterId=' + clusterId" code="create">添加节点</menu-item>
|
<menu-item :href="'/clusters/cluster/createNode?clusterId=' + clusterId" code="create">创建节点</menu-item>
|
||||||
<!--<menu-item :href="'/clusters/cluster/node/import?clusterId=' + clusterId" code="import">批量导入</menu-item>-->
|
|
||||||
<menu-item :href="'/clusters/cluster/installNodes?clusterId=' + clusterId" code="install">安装节点</menu-item>
|
<menu-item :href="'/clusters/cluster/installNodes?clusterId=' + clusterId" code="install">安装节点</menu-item>
|
||||||
</second-menu>
|
</second-menu>
|
||||||
7
web/views/@default/clusters/cluster/createBatch.css
Normal file
7
web/views/@default/clusters/cluster/createBatch.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=createBatch.css.map */
|
||||||
1
web/views/@default/clusters/cluster/createBatch.css.map
Normal file
1
web/views/@default/clusters/cluster/createBatch.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["createBatch.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"createBatch.css"}
|
||||||
19
web/views/@default/clusters/cluster/createBatch.html
Normal file
19
web/views/@default/clusters/cluster/createBatch.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "/clusters/cluster/menu"}
|
||||||
|
{$template "/left_menu"}
|
||||||
|
|
||||||
|
<div class="right-box">
|
||||||
|
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
|
<input type="hidden" name="clusterId" :value="clusterId"/>
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td class="title">节点IP列表</td>
|
||||||
|
<td>
|
||||||
|
<textarea name="ipList" rows="20" placeholder="IP列表,每行一个IP" ref="ipList"></textarea>
|
||||||
|
<p class="comment">每行一个节点IP。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
7
web/views/@default/clusters/cluster/createBatch.js
Normal file
7
web/views/@default/clusters/cluster/createBatch.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.success = NotifySuccess("保存成功", "/clusters/cluster?clusterId=" + this.clusterId)
|
||||||
|
|
||||||
|
this.$delay(function () {
|
||||||
|
this.$refs.ipList.focus()
|
||||||
|
})
|
||||||
|
})
|
||||||
7
web/views/@default/clusters/cluster/createBatch.less
Normal file
7
web/views/@default/clusters/cluster/createBatch.less
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
7
web/views/@default/clusters/cluster/createNode.css
Normal file
7
web/views/@default/clusters/cluster/createNode.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=createNode.css.map */
|
||||||
1
web/views/@default/clusters/cluster/createNode.css.map
Normal file
1
web/views/@default/clusters/cluster/createNode.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["createNode.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"createNode.css"}
|
||||||
49
web/views/@default/clusters/cluster/createNode.html
Normal file
49
web/views/@default/clusters/cluster/createNode.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "/clusters/cluster/menu"}
|
||||||
|
{$template "/left_menu"}
|
||||||
|
|
||||||
|
<div class="right-box">
|
||||||
|
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
|
<input type="hidden" name="clusterId" :value="clusterId"/>
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td class="title">节点名称 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="name" maxlength="50" ref="focus"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IP地址</td>
|
||||||
|
<td>
|
||||||
|
<node-ip-addresses-box></node-ip-addresses-box>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||||
|
</tr>
|
||||||
|
<tbody v-show="moreOptionsVisible">
|
||||||
|
<tr>
|
||||||
|
<td>SSH主机地址</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="sshHost" maxlength="64"/>
|
||||||
|
<p class="comment">比如192.168.1.100</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>SSH主机端口</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="sshPort" maxlength="5"/>
|
||||||
|
<p class="comment">常见的比如22。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>SSH登录认证</td>
|
||||||
|
<td>
|
||||||
|
<grant-selector></grant-selector>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
7
web/views/@default/clusters/cluster/createNode.less
Normal file
7
web/views/@default/clusters/cluster/createNode.less
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
7
web/views/@default/clusters/cluster/installNodes.css
Normal file
7
web/views/@default/clusters/cluster/installNodes.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=installNodes.css.map */
|
||||||
1
web/views/@default/clusters/cluster/installNodes.css.map
Normal file
1
web/views/@default/clusters/cluster/installNodes.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["installNodes.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"installNodes.css"}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
|
{$template "menu"}
|
||||||
|
{$template "/left_menu"}
|
||||||
|
|
||||||
{$template "menu"}
|
<div class="right-box">
|
||||||
<p>可以通过节点安装包中的<code-label>configs/cluster.yaml</code-label>直接自动注册节点。</p>
|
<p>在官网下载节点安装包,然后通过修改节点安装包中的<code-label>configs/cluster.yaml</code-label>,启动后会自动注册节点。</p>
|
||||||
<table class="ui table definition selectable">
|
<table class="ui table definition selectable">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">cluster.yaml<br/>
|
<td class="title">cluster.yaml<br/>
|
||||||
@@ -9,9 +11,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<pre id="cluster-config-box">rpc:
|
<pre id="cluster-config-box">rpc:
|
||||||
endpoints: [ {{cluster.endpoints}} ]
|
endpoints: [ {{cluster.endpoints}} ]
|
||||||
clusterId: "{{cluster.uniqueId}}"
|
clusterId: "{{cluster.uniqueId}}"
|
||||||
secret: "{{cluster.secret}}"</pre>
|
secret: "{{cluster.secret}}"</pre>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|||||||
7
web/views/@default/clusters/cluster/installNodes.less
Normal file
7
web/views/@default/clusters/cluster/installNodes.less
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
7
web/views/@default/clusters/cluster/installRemote.css
Normal file
7
web/views/@default/clusters/cluster/installRemote.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=installRemote.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["installRemote.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"installRemote.css"}
|
||||||
48
web/views/@default/clusters/cluster/installRemote.html
Normal file
48
web/views/@default/clusters/cluster/installRemote.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "menu"}
|
||||||
|
{$template "/left_menu"}
|
||||||
|
|
||||||
|
<div class="right-box">
|
||||||
|
<p class="comment" v-if="nodes.length == 0">暂时没有需要远程安装的节点。</p>
|
||||||
|
|
||||||
|
<div v-if="nodes.length > 0">
|
||||||
|
<h3>所有未安装节点</h3>
|
||||||
|
<table class="ui table selectable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>节点名</th>
|
||||||
|
<th>访问IP</th>
|
||||||
|
<th>SSH地址</th>
|
||||||
|
<th class="four wide">节点状态</th>
|
||||||
|
<th class="two op">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr v-for="node in nodes">
|
||||||
|
<td>{{node.name}}</td>
|
||||||
|
<td>
|
||||||
|
<span v-for="addr in node.addresses" v-if="addr.canAccess" class="ui label tiny">{{addr.ip}}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="node.login != null && node.login.type == 'ssh' && node.loginParams != null && node.loginParams.host != null && node.loginParams.host.length > 0">
|
||||||
|
{{node.loginParams.host}}:{{node.loginParams.port}}
|
||||||
|
</span>
|
||||||
|
<span v-else class="disabled">没有设置</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div v-if="node.installStatus != null && (node.installStatus.isRunning || node.installStatus.isFinished)">
|
||||||
|
<div v-if="node.installStatus.isRunning" class="blue">安装中...</div>
|
||||||
|
<div v-if="node.installStatus.isFinished">
|
||||||
|
<span v-if="node.installStatus.isOk" class="green">已安装成功</span>
|
||||||
|
<span v-if="!node.installStatus.isOk" class="red">安装过程中发生错误:{{node.installStatus.error}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="" @click.prevent="installNode(node)" v-if="!isInstalling">安装</a>
|
||||||
|
<span v-if="isInstalling && node.isInstalling">安装中...</span>
|
||||||
|
<span v-if="isInstalling && !node.isInstalling" class="disabled">安装</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
73
web/views/@default/clusters/cluster/installRemote.js
Normal file
73
web/views/@default/clusters/cluster/installRemote.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.isInstalling = false
|
||||||
|
let installingNode = null
|
||||||
|
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reload()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.installNode = function (node) {
|
||||||
|
let that = this
|
||||||
|
teaweb.confirm("确定要开始安装此节点吗?", function () {
|
||||||
|
installingNode = node
|
||||||
|
that.isInstalling = true
|
||||||
|
node.isInstalling = true
|
||||||
|
|
||||||
|
that.$post("$")
|
||||||
|
.params({
|
||||||
|
nodeId: node.id
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
default:
|
||||||
|
teaweb.warn("安装失败:" + errMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
setTimeout(this.reload, 3000)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setTimeout(this.reload, 3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
7
web/views/@default/clusters/cluster/installRemote.less
Normal file
7
web/views/@default/clusters/cluster/installRemote.less
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
{$layout}
|
|
||||||
|
|
||||||
{$template "/clusters/cluster/menu"}
|
|
||||||
|
|
||||||
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
|
||||||
<input type="hidden" name="clusterId" :value="clusterId"/>
|
|
||||||
<table class="ui table definition selectable">
|
|
||||||
<tr>
|
|
||||||
<td class="title">节点名称 *</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="name" maxlength="50" ref="focus"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>IP地址</td>
|
|
||||||
<td>
|
|
||||||
<node-ip-addresses-box></node-ip-addresses-box>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>SSH主机地址</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="sshHost" maxlength="64"/>
|
|
||||||
<p class="comment">比如192.168.1.100</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>SSH主机端口</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="sshPort" maxlength="5"/>
|
|
||||||
<p class="comment">常见的比如22。</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>SSH登录认证</td>
|
|
||||||
<td>
|
|
||||||
<grant-selector></grant-selector>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<submit-btn></submit-btn>
|
|
||||||
</form>
|
|
||||||
31
web/views/@default/clusters/cluster/updateNodeSSH.html
Normal file
31
web/views/@default/clusters/cluster/updateNodeSSH.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{$layout "layout_popup"}
|
||||||
|
|
||||||
|
<h3>修改节点"{{node.name}}"的SSH登录信息</h3>
|
||||||
|
|
||||||
|
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
|
<input type="hidden" name="nodeId" :value="node.id"/>
|
||||||
|
<input type="hidden" name="loginId" :value="loginId"/>
|
||||||
|
<table class="ui table definition">
|
||||||
|
<tr>
|
||||||
|
<td class="title">SSH主机地址 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="sshHost" maxlength="64" v-model="params.host" ref="focus"/>
|
||||||
|
<p class="comment">比如192.168.1.100</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>SSH主机端口 &</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="sshPort" maxlength="5" v-model="params.port" style="width:6em"/>
|
||||||
|
<p class="comment">比如22。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>SSH登录认证 *</td>
|
||||||
|
<td>
|
||||||
|
<grant-selector :v-grant="grant"></grant-selector>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
3
web/views/@default/clusters/cluster/updateNodeSSH.js
Normal file
3
web/views/@default/clusters/cluster/updateNodeSSH.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.success = NotifyPopup
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user