mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-04 05:00:25 +08:00
实现最基本的域名服务节点管理
This commit is contained in:
@@ -356,6 +356,10 @@ func (this *RPCClient) NSClusterRPC() pb.NSClusterServiceClient {
|
|||||||
return pb.NewNSClusterServiceClient(this.pickConn())
|
return pb.NewNSClusterServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) NSNodeRPC() pb.NSNodeServiceClient {
|
||||||
|
return pb.NewNSNodeServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
// Context 构造Admin上下文
|
// Context 构造Admin上下文
|
||||||
func (this *RPCClient) Context(adminId int64) context.Context {
|
func (this *RPCClient) Context(adminId int64) context.Context {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type ParentAction struct {
|
|||||||
rpcClient *rpc.RPCClient
|
rpcClient *rpc.RPCClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// 可以调用自身的一个简便方法
|
// Parent 可以调用自身的一个简便方法
|
||||||
func (this *ParentAction) Parent() *ParentAction {
|
func (this *ParentAction) Parent() *ParentAction {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ func (this *ParentAction) CreateLogInfo(description string, args ...interface{})
|
|||||||
this.CreateLog(oplogs.LevelInfo, description, args...)
|
this.CreateLog(oplogs.LevelInfo, description, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取RPC
|
// RPC 获取RPC
|
||||||
func (this *ParentAction) RPC() *rpc.RPCClient {
|
func (this *ParentAction) RPC() *rpc.RPCClient {
|
||||||
if this.rpcClient != nil {
|
if this.rpcClient != nil {
|
||||||
return this.rpcClient
|
return this.rpcClient
|
||||||
@@ -114,7 +114,7 @@ func (this *ParentAction) RPC() *rpc.RPCClient {
|
|||||||
return rpcClient
|
return rpcClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取Context
|
// AdminContext 获取Context
|
||||||
func (this *ParentAction) AdminContext() context.Context {
|
func (this *ParentAction) AdminContext() context.Context {
|
||||||
if this.rpcClient == nil {
|
if this.rpcClient == nil {
|
||||||
rpcClient, err := rpc.SharedRPC()
|
rpcClient, err := rpc.SharedRPC()
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 提示服务器错误信息
|
// Fail 提示服务器错误信息
|
||||||
func Fail(action actions.ActionWrapper, err error) {
|
func Fail(action actions.ActionWrapper, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
||||||
}
|
}
|
||||||
action.Object().Fail(teaconst.ErrServer)
|
action.Object().Fail(teaconst.ErrServer + "(" + err.Error() + ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提示页面错误信息
|
// FailPage 提示页面错误信息
|
||||||
func FailPage(action actions.ActionWrapper, err error) {
|
func FailPage(action actions.ActionWrapper, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
||||||
@@ -43,12 +43,12 @@ func FailPage(action actions.ActionWrapper, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断动作的文件路径是否相当
|
// MatchPath 判断动作的文件路径是否相当
|
||||||
func MatchPath(action *actions.ActionObject, path string) bool {
|
func MatchPath(action *actions.ActionObject, path string) bool {
|
||||||
return action.Request.URL.Path == path
|
return action.Request.URL.Path == path
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找父级Action
|
// FindParentAction 查找父级Action
|
||||||
func FindParentAction(actionPtr actions.ActionWrapper) *ParentAction {
|
func FindParentAction(actionPtr actions.ActionWrapper) *ParentAction {
|
||||||
parentActionValue := reflect.ValueOf(actionPtr).Elem().FieldByName("ParentAction")
|
parentActionValue := reflect.ValueOf(actionPtr).Elem().FieldByName("ParentAction")
|
||||||
if parentActionValue.IsValid() {
|
if parentActionValue.IsValid() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package cluster
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
@@ -90,6 +91,7 @@ func (this *CreateBatchAction) RunPost(params struct {
|
|||||||
nodeId := resp.NodeId
|
nodeId := resp.NodeId
|
||||||
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
|
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
|
||||||
NodeId: nodeId,
|
NodeId: nodeId,
|
||||||
|
Role: nodeconfigs.NodeRoleNode,
|
||||||
Name: "IP地址",
|
Name: "IP地址",
|
||||||
Ip: ip,
|
Ip: ip,
|
||||||
CanAccess: true,
|
CanAccess: true,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -156,6 +157,7 @@ func (this *CreateNodeAction) RunPost(params struct {
|
|||||||
} else {
|
} else {
|
||||||
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
|
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
|
||||||
NodeId: nodeId,
|
NodeId: nodeId,
|
||||||
|
Role: nodeconfigs.NodeRoleNode,
|
||||||
Name: address.GetString("name"),
|
Name: address.GetString("name"),
|
||||||
Ip: address.GetString("ip"),
|
Ip: address.GetString("ip"),
|
||||||
CanAccess: address.GetBool("canAccess"),
|
CanAccess: address.GetBool("canAccess"),
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
ActiveState: types.Int32(params.ActiveState),
|
ActiveState: types.Int32(params.ActiveState),
|
||||||
Keyword: params.Keyword,
|
Keyword: params.Keyword,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
nodeMaps := []maps.Map{}
|
nodeMaps := []maps.Map{}
|
||||||
for _, node := range nodesResp.Nodes {
|
for _, node := range nodesResp.Nodes {
|
||||||
// 状态
|
// 状态
|
||||||
@@ -87,7 +91,10 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IP
|
// IP
|
||||||
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{NodeId: node.Id})
|
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
|
||||||
|
NodeId: node.Id,
|
||||||
|
Role: nodeconfigs.NodeRoleNode,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func init() {
|
|||||||
GetPost("/installManual", new(InstallManualAction)).
|
GetPost("/installManual", new(InstallManualAction)).
|
||||||
|
|
||||||
// 节点相关
|
// 节点相关
|
||||||
Get("/node", new(node.NodeAction)).
|
Get("/node", new(node.IndexAction)).
|
||||||
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)).
|
||||||
|
|||||||
@@ -12,16 +12,16 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodeAction struct {
|
type IndexAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *NodeAction) Init() {
|
func (this *IndexAction) Init() {
|
||||||
this.Nav("", "node", "node")
|
this.Nav("", "node", "node")
|
||||||
this.SecondMenu("nodes")
|
this.SecondMenu("nodes")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *NodeAction) RunGet(params struct {
|
func (this *IndexAction) RunGet(params struct {
|
||||||
NodeId int64
|
NodeId int64
|
||||||
}) {
|
}) {
|
||||||
this.Data["nodeId"] = params.NodeId
|
this.Data["nodeId"] = params.NodeId
|
||||||
@@ -56,7 +56,10 @@ func (this *NodeAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IP地址
|
// IP地址
|
||||||
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{NodeId: params.NodeId})
|
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
Role: nodeconfigs.NodeRoleNode,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
@@ -46,7 +47,10 @@ func (this *UpdateAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IP地址
|
// IP地址
|
||||||
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{NodeId: params.NodeId})
|
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
Role: nodeconfigs.NodeRoleNode,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
@@ -340,14 +344,17 @@ func (this *UpdateAction) RunPost(params struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 禁用老的IP地址
|
// 禁用老的IP地址
|
||||||
_, err = this.RPC().NodeIPAddressRPC().DisableAllIPAddressesWithNodeId(this.AdminContext(), &pb.DisableAllIPAddressesWithNodeIdRequest{NodeId: params.NodeId})
|
_, err = this.RPC().NodeIPAddressRPC().DisableAllIPAddressesWithNodeId(this.AdminContext(), &pb.DisableAllIPAddressesWithNodeIdRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
Role: nodeconfigs.NodeRoleNode,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加新的IP地址
|
// 添加新的IP地址
|
||||||
err = ipaddressutils.UpdateNodeIPAddresses(this.Parent(), params.NodeId, params.IPAddressesJSON)
|
err = ipaddressutils.UpdateNodeIPAddresses(this.Parent(), params.NodeId, nodeconfigs.NodeRoleNode, params.IPAddressesJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -186,7 +186,10 @@ func (this *IndexAction) searchNodes(keyword string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IP
|
// IP
|
||||||
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{NodeId: node.Id})
|
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
|
||||||
|
NodeId: node.Id,
|
||||||
|
Role: nodeconfigs.NodeRoleNode,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@@ -13,14 +12,14 @@ type DeleteAction struct {
|
|||||||
func (this *DeleteAction) RunPost(params struct {
|
func (this *DeleteAction) RunPost(params struct {
|
||||||
NodeId int64
|
NodeId int64
|
||||||
}) {
|
}) {
|
||||||
|
// 创建日志
|
||||||
|
defer this.CreateLogInfo("删除节点", params.NodeId)
|
||||||
|
|
||||||
_, err := this.RPC().NodeRPC().DeleteNode(this.AdminContext(), &pb.DeleteNodeRequest{NodeId: params.NodeId})
|
_, err := this.RPC().NodeRPC().DeleteNode(this.AdminContext(), &pb.DeleteNodeRequest{NodeId: params.NodeId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建日志
|
|
||||||
defer this.CreateLog(oplogs.LevelInfo, "删除节点", params.NodeId)
|
|
||||||
|
|
||||||
this.Success()
|
this.Success()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package ipaddressutils
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 保存一组IP地址
|
// UpdateNodeIPAddresses 保存一组IP地址
|
||||||
func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, ipAddressesJSON []byte) error {
|
func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, role nodeconfigs.NodeRole, ipAddressesJSON []byte) error {
|
||||||
addresses := []maps.Map{}
|
addresses := []maps.Map{}
|
||||||
err := json.Unmarshal(ipAddressesJSON, &addresses)
|
err := json.Unmarshal(ipAddressesJSON, &addresses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -29,6 +30,7 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64,
|
|||||||
} else {
|
} else {
|
||||||
_, err = parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{
|
_, err = parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{
|
||||||
NodeId: nodeId,
|
NodeId: nodeId,
|
||||||
|
Role: role,
|
||||||
Name: addr.GetString("name"),
|
Name: addr.GetString("name"),
|
||||||
Ip: addr.GetString("ip"),
|
Ip: addr.GetString("ip"),
|
||||||
CanAccess: addr.GetBool("canAccess"),
|
CanAccess: addr.GetBool("canAccess"),
|
||||||
|
|||||||
100
internal/web/actions/default/ns/clusters/cluster/createNode.go
Normal file
100
internal/web/actions/default/ns/clusters/cluster/createNode.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateNodeAction 创建节点
|
||||||
|
type CreateNodeAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreateNodeAction) Init() {
|
||||||
|
this.Nav("", "node", "create")
|
||||||
|
this.SecondMenu("nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreateNodeAction) RunGet(params struct {
|
||||||
|
ClusterId int64
|
||||||
|
}) {
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CreateNodeAction) RunPost(params struct {
|
||||||
|
Name string
|
||||||
|
IpAddressesJSON []byte
|
||||||
|
ClusterId int64
|
||||||
|
|
||||||
|
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("请选择所在集群")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP地址
|
||||||
|
ipAddresses := []maps.Map{}
|
||||||
|
if len(params.IpAddressesJSON) > 0 {
|
||||||
|
err := json.Unmarshal(params.IpAddressesJSON, &ipAddresses)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ipAddresses) == 0 {
|
||||||
|
this.Fail("请至少输入一个IP地址")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
createResp, err := this.RPC().NSNodeRPC().CreateNSNode(this.AdminContext(), &pb.CreateNSNodeRequest{
|
||||||
|
Name: params.Name,
|
||||||
|
NodeClusterId: params.ClusterId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeId := createResp.NsNodeId
|
||||||
|
|
||||||
|
// IP地址
|
||||||
|
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,
|
||||||
|
Role: nodeconfigs.NodeRoleDNS,
|
||||||
|
Name: address.GetString("name"),
|
||||||
|
Ip: address.GetString("ip"),
|
||||||
|
CanAccess: address.GetBool("canAccess"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建日志
|
||||||
|
defer this.CreateLog(oplogs.LevelInfo, "创建域名服务节点 %d", nodeId)
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeleteNodeAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DeleteNodeAction) RunPost(params struct {
|
||||||
|
NodeId int64
|
||||||
|
}) {
|
||||||
|
defer this.CreateLogInfo("删除域名服务节点 %d", params.NodeId)
|
||||||
|
|
||||||
|
_, err := this.RPC().NSNodeRPC().DeleteNSNode(this.AdminContext(), &pb.DeleteNSNodeRequest{NsNodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -2,16 +2,138 @@
|
|||||||
|
|
||||||
package cluster
|
package cluster
|
||||||
|
|
||||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type IndexAction struct {
|
type IndexAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) Init() {
|
func (this *IndexAction) Init() {
|
||||||
this.Nav("", "node", "")
|
this.Nav("", "node", "index")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct{}) {
|
func (this *IndexAction) RunGet(params struct {
|
||||||
|
ClusterId int64
|
||||||
|
InstalledState int
|
||||||
|
ActiveState int
|
||||||
|
Keyword string
|
||||||
|
}) {
|
||||||
|
this.Data["installState"] = params.InstalledState
|
||||||
|
this.Data["activeState"] = params.ActiveState
|
||||||
|
this.Data["keyword"] = params.Keyword
|
||||||
|
|
||||||
|
countAllResp, err := this.RPC().NSNodeRPC().CountAllEnabledNSNodesMatch(this.AdminContext(), &pb.CountAllEnabledNSNodesMatchRequest{
|
||||||
|
NsClusterId: params.ClusterId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["countAll"] = countAllResp.Count
|
||||||
|
|
||||||
|
countResp, err := this.RPC().NSNodeRPC().CountAllEnabledNSNodesMatch(this.AdminContext(), &pb.CountAllEnabledNSNodesMatchRequest{
|
||||||
|
NsClusterId: params.ClusterId,
|
||||||
|
InstallState: types.Int32(params.InstalledState),
|
||||||
|
ActiveState: types.Int32(params.ActiveState),
|
||||||
|
Keyword: params.Keyword,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
page := this.NewPage(countResp.Count)
|
||||||
|
this.Data["page"] = page.AsHTML()
|
||||||
|
|
||||||
|
nodesResp, err := this.RPC().NSNodeRPC().ListEnabledNSNodesMatch(this.AdminContext(), &pb.ListEnabledNSNodesMatchRequest{
|
||||||
|
Offset: page.Offset,
|
||||||
|
Size: page.Size,
|
||||||
|
NsClusterId: params.ClusterId,
|
||||||
|
InstallState: types.Int32(params.InstalledState),
|
||||||
|
ActiveState: types.Int32(params.ActiveState),
|
||||||
|
Keyword: params.Keyword,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeMaps := []maps.Map{}
|
||||||
|
for _, node := range nodesResp.NsNodes {
|
||||||
|
// 状态
|
||||||
|
status := &nodeconfigs.NodeStatus{}
|
||||||
|
if len(node.StatusJSON) > 0 {
|
||||||
|
err = json.Unmarshal(node.StatusJSON, &status)
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP
|
||||||
|
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
|
||||||
|
NodeId: node.Id,
|
||||||
|
Role: nodeconfigs.NodeRoleDNS,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ipAddresses := []maps.Map{}
|
||||||
|
for _, addr := range ipAddressesResp.Addresses {
|
||||||
|
ipAddresses = append(ipAddresses, maps.Map{
|
||||||
|
"id": addr.Id,
|
||||||
|
"name": addr.Name,
|
||||||
|
"ip": addr.Ip,
|
||||||
|
"canAccess": addr.CanAccess,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeMaps = append(nodeMaps, maps.Map{
|
||||||
|
"id": node.Id,
|
||||||
|
"name": node.Name,
|
||||||
|
"isInstalled": node.IsInstalled,
|
||||||
|
"isOn": node.IsOn,
|
||||||
|
"isUp": node.IsUp,
|
||||||
|
"installStatus": maps.Map{
|
||||||
|
"isRunning": node.InstallStatus.IsRunning,
|
||||||
|
"isFinished": node.InstallStatus.IsFinished,
|
||||||
|
"isOk": node.InstallStatus.IsOk,
|
||||||
|
"error": node.InstallStatus.Error,
|
||||||
|
},
|
||||||
|
"status": maps.Map{
|
||||||
|
"isActive": status.IsActive,
|
||||||
|
"updatedAt": status.UpdatedAt,
|
||||||
|
"hostname": status.Hostname,
|
||||||
|
"cpuUsage": status.CPUUsage,
|
||||||
|
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||||
|
"memUsage": status.MemoryUsage,
|
||||||
|
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||||
|
},
|
||||||
|
"ipAddresses": ipAddresses,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["nodes"] = nodeMaps
|
||||||
|
|
||||||
|
// 记录最近访问
|
||||||
|
_, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{
|
||||||
|
ItemType: "nsCluster",
|
||||||
|
ItemId: params.ClusterId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cluster
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/cluster/node"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ns/clusters/clusterutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||||
"github.com/iwind/TeaGo"
|
"github.com/iwind/TeaGo"
|
||||||
@@ -17,6 +18,18 @@ func init() {
|
|||||||
Prefix("/ns/clusters/cluster").
|
Prefix("/ns/clusters/cluster").
|
||||||
Get("", new(IndexAction)).
|
Get("", new(IndexAction)).
|
||||||
GetPost("/delete", new(DeleteAction)).
|
GetPost("/delete", new(DeleteAction)).
|
||||||
|
GetPost("/createNode", new(CreateNodeAction)).
|
||||||
|
Post("/deleteNode", new(DeleteNodeAction)).
|
||||||
|
|
||||||
|
// 节点相关
|
||||||
|
Prefix("/ns/clusters/cluster/node").
|
||||||
|
Get("", new(node.IndexAction)).
|
||||||
|
Get("/logs", new(node.LogsAction)).
|
||||||
|
GetPost("/update", new(node.UpdateAction)).
|
||||||
|
GetPost("/install", new(node.InstallAction)).
|
||||||
|
Post("/status", new(node.StatusAction)).
|
||||||
|
Post("/updateInstallStatus", new(node.UpdateInstallStatusAction)).
|
||||||
|
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
137
internal/web/actions/default/ns/clusters/cluster/node/index.go
Normal file
137
internal/web/actions/default/ns/clusters/cluster/node/index.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IndexAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IndexAction) Init() {
|
||||||
|
this.Nav("", "node", "node")
|
||||||
|
this.SecondMenu("nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IndexAction) RunGet(params struct {
|
||||||
|
NodeId int64
|
||||||
|
}) {
|
||||||
|
this.Data["nodeId"] = params.NodeId
|
||||||
|
|
||||||
|
nodeResp, err := this.RPC().NSNodeRPC().FindEnabledNSNode(this.AdminContext(), &pb.FindEnabledNSNodeRequest{NsNodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
node := nodeResp.NsNode
|
||||||
|
if node == nil {
|
||||||
|
this.WriteString("找不到要操作的节点")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var clusterMap maps.Map = nil
|
||||||
|
if node.NsCluster != nil {
|
||||||
|
clusterId := node.NsCluster.Id
|
||||||
|
clusterResp, err := this.RPC().NSClusterRPC().FindEnabledNSCluster(this.AdminContext(), &pb.FindEnabledNSClusterRequest{NsClusterId: clusterId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cluster := clusterResp.NsCluster
|
||||||
|
if cluster != nil {
|
||||||
|
clusterMap = maps.Map{
|
||||||
|
"id": cluster.Id,
|
||||||
|
"name": cluster.Name,
|
||||||
|
"installDir": cluster.InstallDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP地址
|
||||||
|
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
Role: nodeconfigs.NodeRoleDNS,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ipAddressMaps := []maps.Map{}
|
||||||
|
for _, addr := range ipAddressesResp.Addresses {
|
||||||
|
ipAddressMaps = append(ipAddressMaps, maps.Map{
|
||||||
|
"id": addr.Id,
|
||||||
|
"name": addr.Name,
|
||||||
|
"ip": addr.Ip,
|
||||||
|
"canAccess": addr.CanAccess,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行状态
|
||||||
|
status := &nodeconfigs.NodeStatus{}
|
||||||
|
if len(node.StatusJSON) > 0 {
|
||||||
|
err = json.Unmarshal(node.StatusJSON, &status)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有新版本
|
||||||
|
if len(status.OS) > 0 {
|
||||||
|
checkVersionResp, err := this.RPC().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{
|
||||||
|
Os: status.OS,
|
||||||
|
Arch: status.Arch,
|
||||||
|
CurrentVersion: status.BuildVersion,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion
|
||||||
|
this.Data["newVersion"] = checkVersionResp.NewVersion
|
||||||
|
} else {
|
||||||
|
this.Data["shouldUpgrade"] = false
|
||||||
|
this.Data["newVersion"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["node"] = maps.Map{
|
||||||
|
"id": node.Id,
|
||||||
|
"name": node.Name,
|
||||||
|
"ipAddresses": ipAddressMaps,
|
||||||
|
"cluster": clusterMap,
|
||||||
|
"installDir": node.InstallDir,
|
||||||
|
"isInstalled": node.IsInstalled,
|
||||||
|
"uniqueId": node.UniqueId,
|
||||||
|
"secret": node.Secret,
|
||||||
|
"isOn": node.IsOn,
|
||||||
|
|
||||||
|
"status": maps.Map{
|
||||||
|
"isActive": status.IsActive,
|
||||||
|
"updatedAt": status.UpdatedAt,
|
||||||
|
"hostname": status.Hostname,
|
||||||
|
"cpuUsage": status.CPUUsage,
|
||||||
|
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||||
|
"memUsage": status.MemoryUsage,
|
||||||
|
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||||
|
"connectionCount": status.ConnectionCount,
|
||||||
|
"buildVersion": status.BuildVersion,
|
||||||
|
"cpuPhysicalCount": status.CPUPhysicalCount,
|
||||||
|
"cpuLogicalCount": status.CPULogicalCount,
|
||||||
|
"load1m": fmt.Sprintf("%.2f", status.Load1m),
|
||||||
|
"load5m": fmt.Sprintf("%.2f", status.Load5m),
|
||||||
|
"load15m": fmt.Sprintf("%.2f", status.Load15m),
|
||||||
|
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
|
||||||
|
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
117
internal/web/actions/default/ns/clusters/cluster/node/install.go
Normal file
117
internal/web/actions/default/ns/clusters/cluster/node/install.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
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/maps"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InstallAction 安装节点
|
||||||
|
type InstallAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *InstallAction) Init() {
|
||||||
|
this.Nav("", "node", "install")
|
||||||
|
this.SecondMenu("nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *InstallAction) RunGet(params struct {
|
||||||
|
NodeId int64
|
||||||
|
}) {
|
||||||
|
this.Data["nodeId"] = params.NodeId
|
||||||
|
|
||||||
|
// 节点
|
||||||
|
nodeResp, err := this.RPC().NSNodeRPC().FindEnabledNSNode(this.AdminContext(), &pb.FindEnabledNSNodeRequest{NsNodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
node := nodeResp.NsNode
|
||||||
|
if node == nil {
|
||||||
|
this.WriteString("找不到要操作的节点")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安装信息
|
||||||
|
if node.InstallStatus != nil {
|
||||||
|
this.Data["installStatus"] = maps.Map{
|
||||||
|
"isRunning": node.InstallStatus.IsRunning,
|
||||||
|
"isFinished": node.InstallStatus.IsFinished,
|
||||||
|
"isOk": node.InstallStatus.IsOk,
|
||||||
|
"updatedAt": node.InstallStatus.UpdatedAt,
|
||||||
|
"error": node.InstallStatus.Error,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.Data["installStatus"] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 集群
|
||||||
|
var clusterMap maps.Map = nil
|
||||||
|
if node.NsCluster != nil {
|
||||||
|
clusterId := node.NsCluster.Id
|
||||||
|
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cluster := clusterResp.NodeCluster
|
||||||
|
if cluster != nil {
|
||||||
|
clusterMap = maps.Map{
|
||||||
|
"id": cluster.Id,
|
||||||
|
"name": cluster.Name,
|
||||||
|
"installDir": cluster.InstallDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API节点列表
|
||||||
|
apiNodesResp, err := this.RPC().APINodeRPC().FindAllEnabledAPINodes(this.AdminContext(), &pb.FindAllEnabledAPINodesRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
apiNodes := apiNodesResp.Nodes
|
||||||
|
apiEndpoints := []string{}
|
||||||
|
for _, apiNode := range apiNodes {
|
||||||
|
if !apiNode.IsOn {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
apiEndpoints = append(apiEndpoints, apiNode.AccessAddrs...)
|
||||||
|
}
|
||||||
|
this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\""
|
||||||
|
|
||||||
|
this.Data["node"] = maps.Map{
|
||||||
|
"id": node.Id,
|
||||||
|
"name": node.Name,
|
||||||
|
"installDir": node.InstallDir,
|
||||||
|
"isInstalled": node.IsInstalled,
|
||||||
|
"uniqueId": node.UniqueId,
|
||||||
|
"secret": node.Secret,
|
||||||
|
"cluster": clusterMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunPost 开始安装
|
||||||
|
func (this *InstallAction) RunPost(params struct {
|
||||||
|
NodeId int64
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
// 创建日志
|
||||||
|
defer this.CreateLogInfo("安装节点 %d", params.NodeId)
|
||||||
|
|
||||||
|
_, err := this.RPC().NSNodeRPC().InstallNSNode(this.AdminContext(), &pb.InstallNSNodeRequest{
|
||||||
|
NsNodeId: params.NodeId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogsAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *LogsAction) Init() {
|
||||||
|
this.Nav("", "node", "log")
|
||||||
|
this.SecondMenu("nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *LogsAction) RunGet(params struct {
|
||||||
|
NodeId int64
|
||||||
|
DayFrom string
|
||||||
|
DayTo string
|
||||||
|
Keyword string
|
||||||
|
Level string
|
||||||
|
}) {
|
||||||
|
this.Data["nodeId"] = params.NodeId
|
||||||
|
this.Data["dayFrom"] = params.DayFrom
|
||||||
|
this.Data["dayTo"] = params.DayTo
|
||||||
|
this.Data["keyword"] = params.Keyword
|
||||||
|
this.Data["level"] = params.Level
|
||||||
|
|
||||||
|
countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
|
||||||
|
Role: nodeconfigs.NodeRoleDNS,
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
DayFrom: params.DayFrom,
|
||||||
|
DayTo: params.DayTo,
|
||||||
|
Keyword: params.Keyword,
|
||||||
|
Level: params.Level,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
count := countResp.Count
|
||||||
|
page := this.NewPage(count, 20)
|
||||||
|
|
||||||
|
logsResp, err := this.RPC().NodeLogRPC().ListNodeLogs(this.AdminContext(), &pb.ListNodeLogsRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
Role: nodeconfigs.NodeRoleDNS,
|
||||||
|
DayFrom: params.DayFrom,
|
||||||
|
DayTo: params.DayTo,
|
||||||
|
Keyword: params.Keyword,
|
||||||
|
Level: params.Level,
|
||||||
|
Offset: page.Offset,
|
||||||
|
Size: page.Size,
|
||||||
|
})
|
||||||
|
|
||||||
|
logs := []maps.Map{}
|
||||||
|
for _, log := range logsResp.NodeLogs {
|
||||||
|
logs = append(logs, maps.Map{
|
||||||
|
"tag": log.Tag,
|
||||||
|
"description": log.Description,
|
||||||
|
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", log.CreatedAt),
|
||||||
|
"level": log.Level,
|
||||||
|
"isToday": timeutil.FormatTime("Y-m-d", log.CreatedAt) == timeutil.Format("Y-m-d"),
|
||||||
|
"count": log.Count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["logs"] = logs
|
||||||
|
|
||||||
|
this.Data["page"] = page.AsHTML()
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatusAction 节点状态
|
||||||
|
type StatusAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *StatusAction) RunPost(params struct {
|
||||||
|
NodeId int64
|
||||||
|
}) {
|
||||||
|
// 节点
|
||||||
|
nodeResp, err := this.RPC().NSNodeRPC().FindEnabledNSNode(this.AdminContext(), &pb.FindEnabledNSNodeRequest{NsNodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
node := nodeResp.NsNode
|
||||||
|
if node == nil {
|
||||||
|
this.WriteString("找不到要操作的节点")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安装信息
|
||||||
|
if node.InstallStatus != nil {
|
||||||
|
this.Data["installStatus"] = maps.Map{
|
||||||
|
"isRunning": node.InstallStatus.IsRunning,
|
||||||
|
"isFinished": node.InstallStatus.IsFinished,
|
||||||
|
"isOk": node.InstallStatus.IsOk,
|
||||||
|
"updatedAt": node.InstallStatus.UpdatedAt,
|
||||||
|
"error": node.InstallStatus.Error,
|
||||||
|
"errorCode": node.InstallStatus.ErrorCode,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.Data["installStatus"] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["isInstalled"] = node.IsInstalled
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
164
internal/web/actions/default/ns/clusters/cluster/node/update.go
Normal file
164
internal/web/actions/default/ns/clusters/cluster/node/update.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateAction) Init() {
|
||||||
|
this.Nav("", "node", "update")
|
||||||
|
this.SecondMenu("nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateAction) RunGet(params struct {
|
||||||
|
NodeId int64
|
||||||
|
}) {
|
||||||
|
this.Data["nodeId"] = params.NodeId
|
||||||
|
|
||||||
|
nodeResp, err := this.RPC().NSNodeRPC().FindEnabledNSNode(this.AdminContext(), &pb.FindEnabledNSNodeRequest{NsNodeId: params.NodeId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
node := nodeResp.NsNode
|
||||||
|
if node == nil {
|
||||||
|
this.WriteString("找不到要操作的节点")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var clusterMap maps.Map = nil
|
||||||
|
if node.NsCluster != nil {
|
||||||
|
clusterMap = maps.Map{
|
||||||
|
"id": node.NsCluster.Id,
|
||||||
|
"name": node.NsCluster.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP地址
|
||||||
|
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
Role: nodeconfigs.NodeRoleDNS,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ipAddressMaps := []maps.Map{}
|
||||||
|
for _, addr := range ipAddressesResp.Addresses {
|
||||||
|
ipAddressMaps = append(ipAddressMaps, maps.Map{
|
||||||
|
"id": addr.Id,
|
||||||
|
"name": addr.Name,
|
||||||
|
"ip": addr.Ip,
|
||||||
|
"canAccess": addr.CanAccess,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Data["node"] = maps.Map{
|
||||||
|
"id": node.Id,
|
||||||
|
"name": node.Name,
|
||||||
|
"ipAddresses": ipAddressMaps,
|
||||||
|
"cluster": clusterMap,
|
||||||
|
"isOn": node.IsOn,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有集群
|
||||||
|
resp, err := this.RPC().NSClusterRPC().FindAllEnabledNSClusters(this.AdminContext(), &pb.FindAllEnabledNSClustersRequest{})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clusterMaps := []maps.Map{}
|
||||||
|
for _, cluster := range resp.NsClusters {
|
||||||
|
clusterMaps = append(clusterMaps, maps.Map{
|
||||||
|
"id": cluster.Id,
|
||||||
|
"name": cluster.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.Data["clusters"] = clusterMaps
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateAction) RunPost(params struct {
|
||||||
|
LoginId int64
|
||||||
|
NodeId int64
|
||||||
|
Name string
|
||||||
|
IPAddressesJSON []byte `alias:"ipAddressesJSON"`
|
||||||
|
ClusterId int64
|
||||||
|
IsOn bool
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
// 创建日志
|
||||||
|
defer this.CreateLog(oplogs.LevelInfo, "修改节点 %d", params.NodeId)
|
||||||
|
|
||||||
|
if params.NodeId <= 0 {
|
||||||
|
this.Fail("要操作的节点不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
params.Must.
|
||||||
|
Field("name", params.Name).
|
||||||
|
Require("请输入节点名称")
|
||||||
|
|
||||||
|
// TODO 检查cluster
|
||||||
|
if params.ClusterId <= 0 {
|
||||||
|
this.Fail("请选择所在集群")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP地址
|
||||||
|
ipAddresses := []maps.Map{}
|
||||||
|
if len(params.IPAddressesJSON) > 0 {
|
||||||
|
err := json.Unmarshal(params.IPAddressesJSON, &ipAddresses)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ipAddresses) == 0 {
|
||||||
|
this.Fail("请至少输入一个IP地址")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
_, err := this.RPC().NSNodeRPC().UpdateNSNode(this.AdminContext(), &pb.UpdateNSNodeRequest{
|
||||||
|
NsNodeId: params.NodeId,
|
||||||
|
Name: params.Name,
|
||||||
|
NsClusterId: params.ClusterId,
|
||||||
|
IsOn: params.IsOn,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用老的IP地址
|
||||||
|
_, err = this.RPC().NodeIPAddressRPC().DisableAllIPAddressesWithNodeId(this.AdminContext(), &pb.DisableAllIPAddressesWithNodeIdRequest{
|
||||||
|
NodeId: params.NodeId,
|
||||||
|
Role: nodeconfigs.NodeRoleDNS,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新的IP地址
|
||||||
|
err = ipaddressutils.UpdateNodeIPAddresses(this.Parent(), params.NodeId, nodeconfigs.NodeRoleDNS, params.IPAddressesJSON)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateInstallStatusAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateInstallStatusAction) RunPost(params struct {
|
||||||
|
NodeId int64
|
||||||
|
IsInstalled bool
|
||||||
|
}) {
|
||||||
|
// 创建日志
|
||||||
|
defer this.CreateLog(oplogs.LevelInfo, "修改节点安装状态 %d", params.NodeId)
|
||||||
|
|
||||||
|
_, err := this.RPC().NSNodeRPC().UpdateNSNodeIsInstalled(this.AdminContext(), &pb.UpdateNSNodeIsInstalledRequest{
|
||||||
|
NsNodeId: params.NodeId,
|
||||||
|
IsInstalled: params.IsInstalled,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
@@ -4,8 +4,10 @@ package clusters
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IndexAction struct {
|
type IndexAction struct {
|
||||||
@@ -36,10 +38,37 @@ func (this *IndexAction) RunGet(params struct{}) {
|
|||||||
}
|
}
|
||||||
clusterMaps := []maps.Map{}
|
clusterMaps := []maps.Map{}
|
||||||
for _, cluster := range clustersResp.NsClusters {
|
for _, cluster := range clustersResp.NsClusters {
|
||||||
|
// 全部节点数量
|
||||||
|
countNodesResp, err := this.RPC().NSNodeRPC().CountAllEnabledNSNodesMatch(this.AdminContext(), &pb.CountAllEnabledNSNodesMatchRequest{NsClusterId: cluster.Id})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在线节点
|
||||||
|
countActiveNodesResp, err := this.RPC().NSNodeRPC().CountAllEnabledNSNodesMatch(this.AdminContext(), &pb.CountAllEnabledNSNodesMatchRequest{
|
||||||
|
NsClusterId: cluster.Id,
|
||||||
|
ActiveState: types.Int32(configutils.BoolStateYes),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 需要升级的节点
|
||||||
|
countUpgradeNodesResp, err := this.RPC().NSNodeRPC().CountAllUpgradeNSNodesWithNSClusterId(this.AdminContext(), &pb.CountAllUpgradeNSNodesWithNSClusterIdRequest{NsClusterId: cluster.Id})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
clusterMaps = append(clusterMaps, maps.Map{
|
clusterMaps = append(clusterMaps, maps.Map{
|
||||||
"id": cluster.Id,
|
"id": cluster.Id,
|
||||||
"name": cluster.Name,
|
"name": cluster.Name,
|
||||||
"isOn": cluster.IsOn,
|
"isOn": cluster.IsOn,
|
||||||
|
"countAllNodes": countNodesResp.Count,
|
||||||
|
"countActiveNodes": countActiveNodesResp.Count,
|
||||||
|
"countUpgradeNodes": countUpgradeNodesResp.Count,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.Data["clusters"] = clusterMaps
|
this.Data["clusters"] = clusterMaps
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
a.underline {
|
a.underline {
|
||||||
border-bottom: 1px #db2828 dashed;
|
border-bottom: 1px #db2828 dashed;
|
||||||
}
|
}
|
||||||
/*# sourceMappingURL=node.css.map */
|
/*# sourceMappingURL=index.css.map */
|
||||||
1
web/views/@default/clusters/cluster/node/index.css.map
Normal file
1
web/views/@default/clusters/cluster/node/index.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"index.css"}
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"version":3,"sources":["node.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"node.css"}
|
|
||||||
@@ -9,29 +9,6 @@ Tea.context(function () {
|
|||||||
// IP地址相关
|
// IP地址相关
|
||||||
this.ipAddresses = this.node.ipAddresses;
|
this.ipAddresses = this.node.ipAddresses;
|
||||||
|
|
||||||
// 添加IP地址
|
|
||||||
this.addIPAddress = function () {
|
|
||||||
teaweb.popup("/nodes/ipAddresses/createPopup", {
|
|
||||||
callback: function (resp) {
|
|
||||||
this.ipAddresses.push(resp.data.ipAddress);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// 修改地址
|
|
||||||
this.updateIPAddress = function (index, address) {
|
|
||||||
teaweb.popup("/nodes/ipAddresses/updatePopup?addressId=" + address.id, {
|
|
||||||
callback: function (resp) {
|
|
||||||
Vue.set(this.ipAddresses, index, resp.data.ipAddress);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除IP地址
|
|
||||||
this.removeIPAddress = function (index) {
|
|
||||||
this.ipAddresses.$remove(index);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 认证相关
|
// 认证相关
|
||||||
this.grant = null;
|
this.grant = null;
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,8 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>集群名称</th>
|
<th>集群名称</th>
|
||||||
<th class="center width10">节点数量</th>
|
<th class="center width10">节点数</th>
|
||||||
<th class="center width10">在线节点数量</th>
|
<th class="center width10">在线节点数</th>
|
||||||
<th>DNS域名</th>
|
<th>DNS域名</th>
|
||||||
<th class="two op">操作</th>
|
<th class="two op">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
6
web/views/@default/ns/clusters/cluster/@menu.html
Normal file
6
web/views/@default/ns/clusters/cluster/@menu.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<second-menu>
|
||||||
|
<menu-item :href="'/ns/clusters/cluster?clusterId=' + clusterId" code="index">节点列表</menu-item>
|
||||||
|
<menu-item :href="'/ns/clusters/cluster/createNode?clusterId=' + clusterId" code="create">创建节点</menu-item>
|
||||||
|
<!--<menu-item :href="'/ns/clusters/cluster/installManual?clusterId=' + clusterId" code="install">安装升级</menu-item>
|
||||||
|
<menu-item :href="'/ns/clusters/cluster/groups?clusterId=' + clusterId" code="group">节点分组</menu-item>-->
|
||||||
|
</second-menu>
|
||||||
7
web/views/@default/ns/clusters/cluster/createNode.css
Normal file
7
web/views/@default/ns/clusters/cluster/createNode.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=createNode.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["createNode.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"createNode.css"}
|
||||||
27
web/views/@default/ns/clusters/cluster/createNode.html
Normal file
27
web/views/@default/ns/clusters/cluster/createNode.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "../menu"}
|
||||||
|
|
||||||
|
<form method="post" 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 role="dns"></node-ip-addresses-box>
|
||||||
|
<p class="comment">用于访问节点和域名解析等。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<more-options-indicator></more-options-indicator>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
3
web/views/@default/ns/clusters/cluster/createNode.js
Normal file
3
web/views/@default/ns/clusters/cluster/createNode.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.success = NotifySuccess("保存成功", "/ns/clusters/cluster?clusterId=" + this.clusterId);
|
||||||
|
});
|
||||||
7
web/views/@default/ns/clusters/cluster/createNode.less
Normal file
7
web/views/@default/ns/clusters/cluster/createNode.less
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.left-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-box {
|
||||||
|
top: 10em;
|
||||||
|
}
|
||||||
@@ -1 +1,102 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
|
{$template "menu"}
|
||||||
|
|
||||||
|
<form class="ui form" action="/ns/clusters/cluster" v-show="countAll > 0">
|
||||||
|
<input type="hidden" name="clusterId" :value="clusterId"/>
|
||||||
|
<div class="ui fields inline">
|
||||||
|
<div class="ui field">
|
||||||
|
<select class="ui dropdown" name="installedState" v-model="installState">
|
||||||
|
<option value="0">[安装状态]</option>
|
||||||
|
<option value="1">已安装</option>
|
||||||
|
<option value="2">未安装</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<select class="ui dropdown" name="activeState" v-model="activeState">
|
||||||
|
<option value="0">[在线状态]</option>
|
||||||
|
<option value="1">在线</option>
|
||||||
|
<option value="2">不在线</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<input type="text" name="keyword" placeholder="关键词" v-model="keyword" style="width:10em"/>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<button class="ui button" type="submit">搜索</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div v-if="countAll == 0">
|
||||||
|
<not-found-box message="当前集群下还没有节点,至少添加一个节点后才能提供服务。"></not-found-box>
|
||||||
|
</div>
|
||||||
|
<div v-if="countAll > 0">
|
||||||
|
<p class="comment" v-if="nodes.length == 0">暂时还没有节点。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="ui table selectable celled" v-if="nodes.length > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>节点名称</th>
|
||||||
|
<th>IP</th>
|
||||||
|
<th class="width5 center">CPU</th>
|
||||||
|
<th class="width5 center">内存</th>
|
||||||
|
<!--<th>流量</th>
|
||||||
|
<th>连接数</th>-->
|
||||||
|
<th class="two wide center">状态</th>
|
||||||
|
<th class="two op">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr v-for="node in nodes">
|
||||||
|
<td>{{node.name}}</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="node.ipAddresses.length == 0" class="disabled">-</span>
|
||||||
|
<div v-else class="address-box">
|
||||||
|
<div v-for="addr in node.ipAddresses" style="margin-bottom:0.3em">
|
||||||
|
<div class="ui label tiny basic">{{addr.ip}}
|
||||||
|
<span class="small" v-if="addr.name.length > 0">({{addr.name}}<span v-if="!addr.canAccess">,不可访问</span>)</span>
|
||||||
|
<span class="small" v-if="addr.name.length == 0 && !addr.canAccess">(不可访问)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="center">
|
||||||
|
<span v-if="node.status.isActive" :class="{red:node.status.cpuUsage > 0.80}">{{node.status.cpuUsageText}}</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
|
<td class="center">
|
||||||
|
<span v-if="node.status.isActive" :class="{red:node.status.memUsage > 0.80}">{{node.status.memUsageText}}</span>
|
||||||
|
<span v-else class="disabled">-</span>
|
||||||
|
</td>
|
||||||
|
<td class="center">
|
||||||
|
<div v-if="!node.isUp">
|
||||||
|
<span class="red">健康问题下线</span>
|
||||||
|
<div>
|
||||||
|
<a href="" @click.prevent="upNode(node.id)">[上线]</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!node.isOn">
|
||||||
|
<label-on :v-is-on="node.isOn"></label-on>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="node.isInstalled">
|
||||||
|
<div v-if="node.status.isActive">
|
||||||
|
<span v-if="!node.isSynced" class="red">同步中</span>
|
||||||
|
<span v-else class="green">运行中</span>
|
||||||
|
</div>
|
||||||
|
<span v-else-if="node.status.updatedAt > 0" class="red">已断开</span>
|
||||||
|
<span v-else-if="node.status.updatedAt == 0" class="red">未连接</span>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<span v-if="node.installStatus.isRunning" class="red">安装中</span>
|
||||||
|
<a v-if="node.installStatus.isFinished && !node.installStatus.isOk" :href="'/ns/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id" title="点击看安装错误"><span class="red">安装出错</span></a>
|
||||||
|
<a v-else class="red" :href="'/ns/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id" title="点击进安装界面"><span class="red">未安装</span></a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a :href="'/ns/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">详情</a> <a href="" @click.prevent="deleteNode(node.id)">删除</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="page" v-html="page"></div>
|
||||||
21
web/views/@default/ns/clusters/cluster/index.js
Normal file
21
web/views/@default/ns/clusters/cluster/index.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.deleteNode = function (nodeId) {
|
||||||
|
teaweb.confirm("确定要删除这个节点吗?", function () {
|
||||||
|
this.$post("/ns/clusters/cluster/deleteNode")
|
||||||
|
.params({
|
||||||
|
nodeId: nodeId
|
||||||
|
})
|
||||||
|
.refresh();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.upNode = function (nodeId) {
|
||||||
|
teaweb.confirm("确定要手动上线此节点吗?", function () {
|
||||||
|
this.$post("/ns/clusters/cluster/node/up")
|
||||||
|
.params({
|
||||||
|
nodeId: nodeId
|
||||||
|
})
|
||||||
|
.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
10
web/views/@default/ns/clusters/cluster/node/@node_menu.html
Normal file
10
web/views/@default/ns/clusters/cluster/node/@node_menu.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<second-menu>
|
||||||
|
<menu-item :href="'/ns/clusters/cluster?clusterId=' + clusterId">节点列表</menu-item>
|
||||||
|
<span class="item">|</span>
|
||||||
|
<menu-item :href="'/ns/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + nodeId" code="node">节点详情</menu-item>
|
||||||
|
<!--<menu-item :href="'/ns/clusters/cluster/node/monitor?clusterId=' + clusterId + '&nodeId=' + nodeId" code="monitor">监控图表</menu-item>
|
||||||
|
<menu-item :href="'/ns/clusters/cluster/node/thresholds?clusterId=' + clusterId + '&nodeId=' + nodeId" code="threshold">阈值设置</menu-item>-->
|
||||||
|
<menu-item :href="'/ns/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + nodeId" code="log">运行日志</menu-item>
|
||||||
|
<menu-item :href="'/ns/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + nodeId" code="update">修改设置</menu-item>
|
||||||
|
<menu-item :href="'/ns/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + nodeId" code="install">安装节点</menu-item>
|
||||||
|
</second-menu>
|
||||||
4
web/views/@default/ns/clusters/cluster/node/index.css
Normal file
4
web/views/@default/ns/clusters/cluster/node/index.css
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
a.underline {
|
||||||
|
border-bottom: 1px #db2828 dashed;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=index.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"index.css"}
|
||||||
111
web/views/@default/ns/clusters/cluster/node/index.html
Normal file
111
web/views/@default/ns/clusters/cluster/node/index.html
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
{$layout}
|
||||||
|
|
||||||
|
{$template "node_menu"}
|
||||||
|
|
||||||
|
<h3>节点详情</h3>
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td class="title">节点名称</td>
|
||||||
|
<td>{{node.name}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>状态</td>
|
||||||
|
<td><label-on :v-is-on="node.isOn"></label-on></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IP地址</td>
|
||||||
|
<td>
|
||||||
|
<div v-if="node.ipAddresses.length > 0">
|
||||||
|
<div>
|
||||||
|
<div v-for="(address, index) in node.ipAddresses" class="ui label tiny basic">
|
||||||
|
{{address.ip}}
|
||||||
|
<span class="small" v-if="address.name.length > 0">({{address.name}}<span v-if="!address.canAccess">,不可访问</span>)</span>
|
||||||
|
<span class="small" v-if="address.name.length == 0 && !address.canAccess">(不可访问)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<span class="disabled">暂时还没有填写IP地址。</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
|
<h3>运行状态</h3>
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td class="title">是否在运行</td>
|
||||||
|
<td>
|
||||||
|
<div v-if="node.status.isActive">
|
||||||
|
<span class="green">运行中</span>
|
||||||
|
<a href="" @click.prevent="stopNode()" v-if="!isStopping"><span>[通过SSH停止]</span></a>
|
||||||
|
<span v-if="isStopping">[停止中...]</span>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<span class="red">已断开</span>
|
||||||
|
<a href="" @click.prevent="startNode()" v-if="node.isInstalled && !isStarting"><span>[通过SSH启动]</span></a>
|
||||||
|
<span v-if="node.isInstalled && isStarting">[启动中...]</span>
|
||||||
|
<a v-if="!node.isInstalled" :href="'/ns/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id" ><span>去安装></span></a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tbody v-show="node.status.isActive">
|
||||||
|
<tr>
|
||||||
|
<td>CPU用量</td>
|
||||||
|
<td>{{node.status.cpuUsageText}} <span v-if="node.status.cpuPhysicalCount > 0" class="small grey">({{node.status.cpuPhysicalCount}}核心/{{node.status.cpuLogicalCount}}线程)</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>内存用量</td>
|
||||||
|
<td>{{node.status.memUsageText}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>连接数</td>
|
||||||
|
<td>{{node.status.connectionCount}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>负载</td>
|
||||||
|
<td>{{node.status.load1m}} {{node.status.load5m}} {{node.status.load15m}} <tip-icon content="三个数字分别代表1分钟、5分钟、15分钟平均负载"></tip-icon></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>缓存用量</td>
|
||||||
|
<td>
|
||||||
|
磁盘:{{node.status.cacheTotalDiskSize}} 内存:{{node.status.cacheTotalMemorySize}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>版本</td>
|
||||||
|
<td>v{{node.status.buildVersion}}
|
||||||
|
<a :href="'/ns/clusters/cluster/upgradeRemote?clusterId=' + clusterId" v-if="shouldUpgrade"><span class="red">发现新版本v{{newVersion}} »</span></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="comment" v-if="node.status.isActive">每隔30秒钟更新一次运行状态。</p>
|
||||||
|
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<h3>安装信息</h3>
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td>节点ID<em>(id)</em></td>
|
||||||
|
<td>{{node.uniqueId}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>密钥<em>(secret)</em></td>
|
||||||
|
<td>{{node.secret}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="title">安装目录</td>
|
||||||
|
<td>
|
||||||
|
<div v-if="node.installDir.length == 0">使用集群设置<span v-if="node.cluster != null && node.cluster.installDir.length > 0">({{node.cluster.installDir}})</span></div>
|
||||||
|
<span v-else>{{node.installDir}}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>是否已安装</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="node.isInstalled" class="green">已安装</span>
|
||||||
|
<a v-else :href="'/ns/clusters/cluster/installNode?clusterId=' + clusterId + '&nodeId=' + nodeId" class="underline" title="点击进入安装界面"><span class="red">未安装</span></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
35
web/views/@default/ns/clusters/cluster/node/index.js
Normal file
35
web/views/@default/ns/clusters/cluster/node/index.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.isStarting = false
|
||||||
|
this.startNode = function () {
|
||||||
|
this.isStarting = true
|
||||||
|
this.$post("/ns/clusters/cluster/node/start")
|
||||||
|
.params({
|
||||||
|
nodeId: this.node.id
|
||||||
|
})
|
||||||
|
.success(function () {
|
||||||
|
teaweb.success("启动成功", function () {
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
this.isStarting = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isStopping = false
|
||||||
|
this.stopNode = function () {
|
||||||
|
this.isStopping = true
|
||||||
|
this.$post("/ns/clusters/cluster/node/stop")
|
||||||
|
.params({
|
||||||
|
nodeId: this.node.id
|
||||||
|
})
|
||||||
|
.success(function () {
|
||||||
|
teaweb.success("执行成功", function () {
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
this.isStopping = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
3
web/views/@default/ns/clusters/cluster/node/index.less
Normal file
3
web/views/@default/ns/clusters/cluster/node/index.less
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
a.underline {
|
||||||
|
border-bottom: 1px #db2828 dashed;
|
||||||
|
}
|
||||||
7
web/views/@default/ns/clusters/cluster/node/install.css
Normal file
7
web/views/@default/ns/clusters/cluster/node/install.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.installing-box {
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
.installing-box .blue {
|
||||||
|
color: #2185d0;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=install.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["install.less"],"names":[],"mappings":"AAAA;EACC,gBAAA;;AADD,eAGC;EACC,cAAA","file":"install.css"}
|
||||||
54
web/views/@default/ns/clusters/cluster/node/install.html
Normal file
54
web/views/@default/ns/clusters/cluster/node/install.html
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "node_menu"}
|
||||||
|
{$template "/code_editor"}
|
||||||
|
|
||||||
|
<!-- 已安装 -->
|
||||||
|
<div v-if="node.isInstalled">
|
||||||
|
<div class="ui message green">当前节点为已安装状态。</div>
|
||||||
|
<a href="" @click.prevent="updateNodeIsInstalled(false)">[修改为未安装状态]</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 未安装 -->
|
||||||
|
<div v-if="!node.isInstalled">
|
||||||
|
<h3>方法1:通过SSH自动安装</h3>
|
||||||
|
|
||||||
|
<div v-if="installStatus != null && (installStatus.isRunning || installStatus.isFinished)"
|
||||||
|
class="ui segment installing-box">
|
||||||
|
<div v-if="installStatus.isRunning" class="blue">安装中...</div>
|
||||||
|
<div v-if="installStatus.isFinished">
|
||||||
|
<span v-if="installStatus.isOk" class="green">已安装成功</span>
|
||||||
|
<span v-if="!installStatus.isOk" class="red">安装过程中发生错误:{{installStatus.error}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="installStatus != null && installStatus.isFinished">
|
||||||
|
<button class="ui button small" type="button" @click.prevent="install()">重新安装</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="installStatus == null || (!installStatus.isFinished && !installStatus.isRunning)">
|
||||||
|
<button class="ui button small" type="button" @click.prevent="install()">开始安装</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>方法2:手动安装</h3>
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td>配置文件<em>(configs/api.yaml)</em><br/>
|
||||||
|
<em><download-link :v-element="'rpc-code'" :v-file="'api.yaml'">[下载]</download-link ></em></td>
|
||||||
|
<td>
|
||||||
|
<source-code-box id="rpc-code" type="text/yaml">rpc:
|
||||||
|
endpoints: [ {{apiEndpoints}} ]
|
||||||
|
nodeId: "{{node.uniqueId}}"
|
||||||
|
secret: "{{node.secret}}"</source-code-box>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="title">安装目录</td>
|
||||||
|
<td>
|
||||||
|
<div v-if="node.installDir.length == 0">使用集群设置<span
|
||||||
|
v-if="node.cluster != null && node.cluster.installDir.length > 0">({{node.cluster.installDir}})</span>
|
||||||
|
</div>
|
||||||
|
<span v-else>{{node.installDir}}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<a href="" @click.prevent="updateNodeIsInstalled(true)">[修改为已安装状态]</a>
|
||||||
|
</div>
|
||||||
103
web/views/@default/ns/clusters/cluster/node/install.js
Normal file
103
web/views/@default/ns/clusters/cluster/node/install.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
let isInstalling = false
|
||||||
|
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadStatus(this.nodeId)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 开始安装
|
||||||
|
this.install = function () {
|
||||||
|
isInstalling = true
|
||||||
|
|
||||||
|
this.$post("$")
|
||||||
|
.params({
|
||||||
|
nodeId: this.nodeId
|
||||||
|
})
|
||||||
|
.success(function () {
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置节点安装状态
|
||||||
|
this.updateNodeIsInstalled = function (isInstalled) {
|
||||||
|
teaweb.confirm("确定要将当前节点修改为未安装状态?", function () {
|
||||||
|
this.$post("/ns/clusters/cluster/node/updateInstallStatus")
|
||||||
|
.params({
|
||||||
|
nodeId: this.nodeId,
|
||||||
|
isInstalled: isInstalled ? 1 : 0
|
||||||
|
})
|
||||||
|
.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新状态
|
||||||
|
this.reloadStatus = function (nodeId) {
|
||||||
|
let that = this
|
||||||
|
|
||||||
|
this.$post("/ns/clusters/cluster/node/status")
|
||||||
|
.params({
|
||||||
|
nodeId: nodeId
|
||||||
|
})
|
||||||
|
.success(function (resp) {
|
||||||
|
this.installStatus = resp.data.installStatus
|
||||||
|
this.node.isInstalled = resp.data.isInstalled
|
||||||
|
|
||||||
|
if (!isInstalling) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let nodeId = this.node.id
|
||||||
|
let errMsg = this.installStatus.error
|
||||||
|
|
||||||
|
if (this.installStatus.errorCode.length > 0) {
|
||||||
|
isInstalling = false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.installStatus.errorCode) {
|
||||||
|
case "EMPTY_LOGIN":
|
||||||
|
case "EMPTY_SSH_HOST":
|
||||||
|
case "EMPTY_SSH_PORT":
|
||||||
|
case "EMPTY_GRANT":
|
||||||
|
teaweb.warn("需要填写SSH登录信息", function () {
|
||||||
|
teaweb.popup("/ns/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, {
|
||||||
|
callback: function () {
|
||||||
|
that.install()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
case "SSH_LOGIN_FAILED":
|
||||||
|
teaweb.warn("SSH登录失败,请检查设置", function () {
|
||||||
|
teaweb.popup("/ns/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, {
|
||||||
|
callback: function () {
|
||||||
|
that.install()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
case "CREATE_ROOT_DIRECTORY_FAILED":
|
||||||
|
teaweb.warn("创建根目录失败,请检查目录权限或者手工创建:" + errMsg)
|
||||||
|
return
|
||||||
|
case "INSTALL_HELPER_FAILED":
|
||||||
|
teaweb.warn("安装助手失败:" + errMsg)
|
||||||
|
return
|
||||||
|
case "TEST_FAILED":
|
||||||
|
teaweb.warn("环境测试失败:" + errMsg)
|
||||||
|
return
|
||||||
|
case "RPC_TEST_FAILED":
|
||||||
|
teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ",<br/>现在修改API信息?", function () {
|
||||||
|
window.location = "/api"
|
||||||
|
})
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
shouldReload = true
|
||||||
|
//teaweb.warn("安装失败:" + errMsg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadStatus(nodeId)
|
||||||
|
}, 1000)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
7
web/views/@default/ns/clusters/cluster/node/install.less
Normal file
7
web/views/@default/ns/clusters/cluster/node/install.less
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.installing-box {
|
||||||
|
line-height: 1.8;
|
||||||
|
|
||||||
|
.blue {
|
||||||
|
color: #2185d0;
|
||||||
|
}
|
||||||
|
}
|
||||||
5
web/views/@default/ns/clusters/cluster/node/logs.css
Normal file
5
web/views/@default/ns/clusters/cluster/node/logs.css
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pre.log-box {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=logs.css.map */
|
||||||
1
web/views/@default/ns/clusters/cluster/node/logs.css.map
Normal file
1
web/views/@default/ns/clusters/cluster/node/logs.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["logs.less"],"names":[],"mappings":"AAAA,GAAG;EACF,SAAA;EACA,UAAA","file":"logs.css"}
|
||||||
58
web/views/@default/ns/clusters/cluster/node/logs.html
Normal file
58
web/views/@default/ns/clusters/cluster/node/logs.html
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "node_menu"}
|
||||||
|
|
||||||
|
{$var "header"}
|
||||||
|
<!-- datepicker -->
|
||||||
|
<script type="text/javascript" src="/js/moment.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/pikaday.js"></script>
|
||||||
|
<link rel="stylesheet" href="/js/pikaday.css"/>
|
||||||
|
<link rel="stylesheet" href="/js/pikaday.theme.css"/>
|
||||||
|
<link rel="stylesheet" href="/js/pikaday.triangle.css"/>
|
||||||
|
{$end}
|
||||||
|
|
||||||
|
<form method="get" action="/ns/clusters/cluster/node/logs" class="ui form" autocomplete="off">
|
||||||
|
<input type="hidden" name="clusterId" :value="clusterId"/>
|
||||||
|
<input type="hidden" name="nodeId" :value="nodeId"/>
|
||||||
|
<div class="ui fields inline">
|
||||||
|
<div class="ui field">
|
||||||
|
<input type="text" name="dayFrom" placeholder="开始日期" v-model="dayFrom" value="" style="width:8em" id="day-from-picker"/>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<input type="text" name="dayTo" placeholder="结束日期" v-model="dayTo" value="" style="width:8em" id="day-to-picker"/>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<select class="ui dropdown" name="level" v-model="level">
|
||||||
|
<option value="">[级别]</option>
|
||||||
|
<option value="error">错误</option>
|
||||||
|
<option value="warning">警告</option>
|
||||||
|
<option value="info">信息</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<input type="text" name="keyword" style="width:10em" v-model="keyword" placeholder="关键词"/>
|
||||||
|
</div>
|
||||||
|
<div class="ui field">
|
||||||
|
<button type="submit" class="ui button">查询</button>
|
||||||
|
</div>
|
||||||
|
<div class="ui field" v-if="dayFrom.length > 0 || dayTo.length > 0 || keyword.length > 0 || level.length > 0">
|
||||||
|
<a :href="'/ns/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + nodeId">[清除条件]</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p class="comment" v-if="logs.length == 0">暂时还没有日志。</p>
|
||||||
|
|
||||||
|
<table class="ui table selectable" v-if="logs.length > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr v-for="log in logs">
|
||||||
|
<td>
|
||||||
|
<pre class="log-box"><span :class="{red:log.level == 'error', orange:log.level == 'warning'}"><span v-if="!log.isToday">[{{log.createdTime}}]</span><strong v-if="log.isToday">[{{log.createdTime}}]</strong>[{{log.tag}}]{{log.description}}</span> <span v-if="log.count > 0" class="ui label tiny" :class="{red:log.level == 'error', orange:log.level == 'warning'}">共{{log.count}}条</span></pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="page" v-html="page"></div>
|
||||||
6
web/views/@default/ns/clusters/cluster/node/logs.js
Normal file
6
web/views/@default/ns/clusters/cluster/node/logs.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.$delay(function () {
|
||||||
|
teaweb.datepicker("day-from-picker")
|
||||||
|
teaweb.datepicker("day-to-picker")
|
||||||
|
})
|
||||||
|
})
|
||||||
4
web/views/@default/ns/clusters/cluster/node/logs.less
Normal file
4
web/views/@default/ns/clusters/cluster/node/logs.less
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pre.log-box {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
47
web/views/@default/ns/clusters/cluster/node/update.html
Normal file
47
web/views/@default/ns/clusters/cluster/node/update.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
{$layout}
|
||||||
|
|
||||||
|
{$template "node_menu"}
|
||||||
|
|
||||||
|
<h3>修改节点</h3>
|
||||||
|
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
|
<input type="hidden" name="nodeId" :value="node.id"/>
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td class="title">节点名称 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="name" maxlength="50" ref="focus" v-model="node.name"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IP地址 *</td>
|
||||||
|
<td>
|
||||||
|
<node-ip-addresses-box :v-ip-addresses="node.ipAddresses"></node-ip-addresses-box>
|
||||||
|
<p class="comment">用于访问节点和域名解析等。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>所属集群</td>
|
||||||
|
<td>
|
||||||
|
<select class="ui dropdown" name="clusterId" style="width:10em" v-model="clusterId">
|
||||||
|
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||||
|
</tr>
|
||||||
|
<tbody v-show="moreOptionsVisible">
|
||||||
|
<tr>
|
||||||
|
<td>是否启用</td>
|
||||||
|
<td>
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" name="isOn" value="1" v-model="node.isOn"/>
|
||||||
|
<label></label>
|
||||||
|
</div>
|
||||||
|
<p class="comment">如果不启用此节点,此节点上的所有服务将不能访问。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
8
web/views/@default/ns/clusters/cluster/node/update.js
Normal file
8
web/views/@default/ns/clusters/cluster/node/update.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.clusterId = 0;
|
||||||
|
if (this.node.cluster != null && this.node.cluster.id > 0) {
|
||||||
|
this.clusterId = this.node.cluster.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.success = NotifySuccess("保存成功", "/ns/clusters/cluster/node?clusterId=" + this.clusterId + "&nodeId=" + this.node.id);
|
||||||
|
});
|
||||||
@@ -7,12 +7,26 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>集群名称</th>
|
<th>集群名称</th>
|
||||||
|
<th class="center width10">节点数</th>
|
||||||
|
<th class="center width10">在线节点数</th>
|
||||||
<th class="two wide">集群状态</th>
|
<th class="two wide">集群状态</th>
|
||||||
<th class="two op">操作</th>
|
<th class="two op">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr v-for="cluster in clusters">
|
<tr v-for="cluster in clusters">
|
||||||
<td>{{cluster.name}}</td>
|
<td>{{cluster.name}}</td>
|
||||||
|
<td class="center">
|
||||||
|
<a :href="'/ns/clusters/cluster?clusterId=' + cluster.id" v-if="cluster.countAllNodes > 0"><span :class="{red:cluster.countAllNodes > cluster.countActiveNodes}">{{cluster.countAllNodes}}</span></a>
|
||||||
|
<span class="disabled" v-else="">-</span>
|
||||||
|
|
||||||
|
<div v-if="cluster.countUpgradeNodes > 0" style="margin-top:0.5em">
|
||||||
|
<a :href="'/ns/clusters/cluster/upgradeRemote?clusterId=' + cluster.id" title="点击进入远程升级页面"><span class="red">有节点需要升级</span></a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="center">
|
||||||
|
<a :href="'/ns/clusters/cluster?clusterId=' + cluster.id + '&activeState=1'" v-if="cluster.countActiveNodes > 0"><span class="green">{{cluster.countActiveNodes}}</span></a>
|
||||||
|
<span class="disabled" v-else>-</span>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<label-on :v-is-on="cluster.isOn"></label-on>
|
<label-on :v-is-on="cluster.isOn"></label-on>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user