mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 13:10:26 +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").
 | 
			
		||||
			Get("", new(IndexAction)).
 | 
			
		||||
			GetPost("/installNodes", new(InstallNodesAction)).
 | 
			
		||||
			GetPost("/installRemote", new(InstallRemoteAction)).
 | 
			
		||||
			Post("/installStatus", new(InstallStatusAction)).
 | 
			
		||||
			GetPost("/delete", new(DeleteAction)).
 | 
			
		||||
			GetPost("/createNode", new(CreateNodeAction)).
 | 
			
		||||
			GetPost("/createBatch", new(CreateBatchAction)).
 | 
			
		||||
			GetPost("/updateNodeSSH", new(UpdateNodeSSHAction)).
 | 
			
		||||
 | 
			
		||||
			// 节点相关
 | 
			
		||||
			Get("/node", new(node.NodeAction)).
 | 
			
		||||
			GetPost("/node/create", new(node.CreateAction)).
 | 
			
		||||
			GetPost("/node/update", new(node.UpdateAction)).
 | 
			
		||||
			GetPost("/node/install", new(node.InstallAction)).
 | 
			
		||||
			Post("/node/updateInstallStatus", new(node.UpdateInstallStatusAction)).
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package cluster
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"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/maps"
 | 
			
		||||
@@ -19,6 +20,19 @@ func (this *InstallNodesAction) Init() {
 | 
			
		||||
func (this *InstallNodesAction) RunGet(params struct {
 | 
			
		||||
	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})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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
 | 
			
		||||
	apiEndpoints := []string{}
 | 
			
		||||
	for _, apiNode := range apiNodes {
 | 
			
		||||
		if !apiNode.IsOn {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		apiEndpoints = append(apiEndpoints, apiNode.AccessAddrs...)
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clusterResp, err := rpcClient.NodeClusterRPC().FindEnabledNodeCluster(rpcClient.Context(action.Context.GetInt64("adminId")), &pb.FindEnabledNodeClusterRequest{ClusterId: clusterId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logs.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	cluster := clusterResp.Cluster
 | 
			
		||||
	if cluster == nil {
 | 
			
		||||
		action.WriteString("can not find cluster")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if clusterId > 0 {
 | 
			
		||||
		clusterResp, err := rpcClient.NodeClusterRPC().FindEnabledNodeCluster(rpcClient.Context(action.Context.GetInt64("adminId")), &pb.FindEnabledNodeClusterRequest{ClusterId: clusterId})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logs.Error(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		cluster := clusterResp.Cluster
 | 
			
		||||
		if cluster == nil {
 | 
			
		||||
			action.WriteString("can not find cluster")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	tabbar := actionutils.NewTabbar()
 | 
			
		||||
	tabbar.Add("集群列表", "", "/clusters", "", false)
 | 
			
		||||
	tabbar.Add("节点", "", "/clusters/cluster?clusterId="+clusterIdString, "server", selectedTabbar == "node")
 | 
			
		||||
	tabbar.Add("设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
 | 
			
		||||
	tabbar.Add("删除", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
 | 
			
		||||
		tabbar := actionutils.NewTabbar()
 | 
			
		||||
		tabbar.Add("集群列表", "", "/clusters", "", false)
 | 
			
		||||
		tabbar.Add("节点", "", "/clusters/cluster?clusterId="+clusterIdString, "server", selectedTabbar == "node")
 | 
			
		||||
		tabbar.Add("设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting")
 | 
			
		||||
		tabbar.Add("删除", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete")
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		m := tabbar.Add("当前集群:"+cluster.Name, "", "/clusters/cluster?clusterId="+clusterIdString, "", false)
 | 
			
		||||
		m["right"] = true
 | 
			
		||||
		{
 | 
			
		||||
			m := tabbar.Add("当前集群:"+cluster.Name, "", "/clusters/cluster?clusterId="+clusterIdString, "", false)
 | 
			
		||||
			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()
 | 
			
		||||
			}, 500)
 | 
			
		||||
		} else {
 | 
			
		||||
			let url = new URL(healthCheckConfig.url)
 | 
			
		||||
			urlProtocol = url.protocol.substring(0, url.protocol.length - 1)
 | 
			
		||||
			urlPort = url.port
 | 
			
		||||
			urlRequestURI = url.pathname
 | 
			
		||||
			if (url.search.length > 0) {
 | 
			
		||||
				urlRequestURI += url.search
 | 
			
		||||
			try {
 | 
			
		||||
				let url = new URL(healthCheckConfig.url)
 | 
			
		||||
				urlProtocol = url.protocol.substring(0, url.protocol.length - 1)
 | 
			
		||||
				urlPort = url.port
 | 
			
		||||
				urlRequestURI = url.pathname
 | 
			
		||||
				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 {
 | 
			
		||||
@@ -81,7 +97,6 @@ Vue.component("health-check-config-box", {
 | 
			
		||||
					return status
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
			console.log(this.healthCheck.statusCodes)
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	template: `<div>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ Vue.component("values-box", {
 | 
			
		||||
	props: ["values", "size", "maxlength", "name"],
 | 
			
		||||
	data: function () {
 | 
			
		||||
		let values = this.values;
 | 
			
		||||
		if (typeof (values) != "object") {
 | 
			
		||||
		if (values == null) {
 | 
			
		||||
			values = [];
 | 
			
		||||
		}
 | 
			
		||||
		return {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
<second-menu>
 | 
			
		||||
	<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/node/import?clusterId=' + clusterId" code="import">批量导入</menu-item>-->
 | 
			
		||||
	<menu-item :href="'/clusters/cluster/createNode?clusterId=' + clusterId" code="create">创建节点</menu-item>
 | 
			
		||||
	<menu-item :href="'/clusters/cluster/installNodes?clusterId=' + clusterId" code="install">安装节点</menu-item>
 | 
			
		||||
</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}
 | 
			
		||||
{$template "menu"}
 | 
			
		||||
{$template "/left_menu"}
 | 
			
		||||
 | 
			
		||||
	{$template "menu"}
 | 
			
		||||
	<p>可以通过节点安装包中的<code-label>configs/cluster.yaml</code-label>直接自动注册节点。</p>
 | 
			
		||||
<div class="right-box">
 | 
			
		||||
	<p>在官网下载节点安装包,然后通过修改节点安装包中的<code-label>configs/cluster.yaml</code-label>,启动后会自动注册节点。</p>
 | 
			
		||||
	<table class="ui table definition selectable">
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td class="title">cluster.yaml<br/>
 | 
			
		||||
@@ -9,9 +11,10 @@
 | 
			
		||||
			</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<pre id="cluster-config-box">rpc:
 | 
			
		||||
  endpoints: [ {{cluster.endpoints}} ]
 | 
			
		||||
clusterId: "{{cluster.uniqueId}}"
 | 
			
		||||
secret: "{{cluster.secret}}"</pre>
 | 
			
		||||
	endpoints: [ {{cluster.endpoints}} ]
 | 
			
		||||
	clusterId: "{{cluster.uniqueId}}"
 | 
			
		||||
	secret: "{{cluster.secret}}"</pre>
 | 
			
		||||
			</td>
 | 
			
		||||
		</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