diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go index ba18db89..5907f77e 100644 --- a/internal/rpc/rpc_client.go +++ b/internal/rpc/rpc_client.go @@ -356,6 +356,10 @@ func (this *RPCClient) NSClusterRPC() pb.NSClusterServiceClient { return pb.NewNSClusterServiceClient(this.pickConn()) } +func (this *RPCClient) NSNodeRPC() pb.NSNodeServiceClient { + return pb.NewNSNodeServiceClient(this.pickConn()) +} + // Context 构造Admin上下文 func (this *RPCClient) Context(adminId int64) context.Context { ctx := context.Background() diff --git a/internal/web/actions/actionutils/parent_action.go b/internal/web/actions/actionutils/parent_action.go index f75f2483..3fbf2310 100644 --- a/internal/web/actions/actionutils/parent_action.go +++ b/internal/web/actions/actionutils/parent_action.go @@ -20,7 +20,7 @@ type ParentAction struct { rpcClient *rpc.RPCClient } -// 可以调用自身的一个简便方法 +// Parent 可以调用自身的一个简便方法 func (this *ParentAction) Parent() *ParentAction { return this } @@ -97,7 +97,7 @@ func (this *ParentAction) CreateLogInfo(description string, args ...interface{}) this.CreateLog(oplogs.LevelInfo, description, args...) } -// 获取RPC +// RPC 获取RPC func (this *ParentAction) RPC() *rpc.RPCClient { if this.rpcClient != nil { return this.rpcClient @@ -114,7 +114,7 @@ func (this *ParentAction) RPC() *rpc.RPCClient { return rpcClient } -// 获取Context +// AdminContext 获取Context func (this *ParentAction) AdminContext() context.Context { if this.rpcClient == nil { rpcClient, err := rpc.SharedRPC() diff --git a/internal/web/actions/actionutils/utils.go b/internal/web/actions/actionutils/utils.go index c549b1e9..9f3edc7e 100644 --- a/internal/web/actions/actionutils/utils.go +++ b/internal/web/actions/actionutils/utils.go @@ -13,15 +13,15 @@ import ( "strings" ) -// 提示服务器错误信息 +// Fail 提示服务器错误信息 func Fail(action actions.ActionWrapper, err error) { if err != nil { 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) { if err != nil { 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 { return action.Request.URL.Path == path } -// 查找父级Action +// FindParentAction 查找父级Action func FindParentAction(actionPtr actions.ActionWrapper) *ParentAction { parentActionValue := reflect.ValueOf(actionPtr).Elem().FieldByName("ParentAction") if parentActionValue.IsValid() { diff --git a/internal/web/actions/default/clusters/cluster/createBatch.go b/internal/web/actions/default/clusters/cluster/createBatch.go index d1e8e00e..a2f578d5 100644 --- a/internal/web/actions/default/clusters/cluster/createBatch.go +++ b/internal/web/actions/default/clusters/cluster/createBatch.go @@ -3,6 +3,7 @@ package cluster import ( "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/lists" @@ -90,6 +91,7 @@ func (this *CreateBatchAction) RunPost(params struct { nodeId := resp.NodeId _, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{ NodeId: nodeId, + Role: nodeconfigs.NodeRoleNode, Name: "IP地址", Ip: ip, CanAccess: true, diff --git a/internal/web/actions/default/clusters/cluster/createNode.go b/internal/web/actions/default/clusters/cluster/createNode.go index 1f0a09fa..15f3343a 100644 --- a/internal/web/actions/default/clusters/cluster/createNode.go +++ b/internal/web/actions/default/clusters/cluster/createNode.go @@ -4,6 +4,7 @@ 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" @@ -156,6 +157,7 @@ func (this *CreateNodeAction) RunPost(params struct { } else { _, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{ NodeId: nodeId, + Role: nodeconfigs.NodeRoleNode, Name: address.GetString("name"), Ip: address.GetString("ip"), CanAccess: address.GetBool("canAccess"), diff --git a/internal/web/actions/default/clusters/cluster/index.go b/internal/web/actions/default/clusters/cluster/index.go index 79b0c853..cfb347b2 100644 --- a/internal/web/actions/default/clusters/cluster/index.go +++ b/internal/web/actions/default/clusters/cluster/index.go @@ -71,6 +71,10 @@ func (this *IndexAction) RunGet(params struct { ActiveState: types.Int32(params.ActiveState), Keyword: params.Keyword, }) + if err != nil { + this.ErrorPage(err) + return + } nodeMaps := []maps.Map{} for _, node := range nodesResp.Nodes { // 状态 @@ -87,7 +91,10 @@ func (this *IndexAction) RunGet(params struct { } // 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 { this.ErrorPage(err) return diff --git a/internal/web/actions/default/clusters/cluster/init.go b/internal/web/actions/default/clusters/cluster/init.go index ddb2398b..4eb61e1f 100644 --- a/internal/web/actions/default/clusters/cluster/init.go +++ b/internal/web/actions/default/clusters/cluster/init.go @@ -30,7 +30,7 @@ func init() { GetPost("/installManual", new(InstallManualAction)). // 节点相关 - Get("/node", new(node.NodeAction)). + Get("/node", new(node.IndexAction)). GetPost("/node/update", new(node.UpdateAction)). GetPost("/node/install", new(node.InstallAction)). Post("/node/updateInstallStatus", new(node.UpdateInstallStatusAction)). diff --git a/internal/web/actions/default/clusters/cluster/node/node.go b/internal/web/actions/default/clusters/cluster/node/index.go similarity index 97% rename from internal/web/actions/default/clusters/cluster/node/node.go rename to internal/web/actions/default/clusters/cluster/node/index.go index deaeae19..2e358c2f 100644 --- a/internal/web/actions/default/clusters/cluster/node/node.go +++ b/internal/web/actions/default/clusters/cluster/node/index.go @@ -12,16 +12,16 @@ import ( "time" ) -type NodeAction struct { +type IndexAction struct { actionutils.ParentAction } -func (this *NodeAction) Init() { +func (this *IndexAction) Init() { this.Nav("", "node", "node") this.SecondMenu("nodes") } -func (this *NodeAction) RunGet(params struct { +func (this *IndexAction) RunGet(params struct { NodeId int64 }) { this.Data["nodeId"] = params.NodeId @@ -56,7 +56,10 @@ func (this *NodeAction) RunGet(params struct { } // 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 { this.ErrorPage(err) return diff --git a/internal/web/actions/default/clusters/cluster/node/update.go b/internal/web/actions/default/clusters/cluster/node/update.go index 47b5ef1f..583b5967 100644 --- a/internal/web/actions/default/clusters/cluster/node/update.go +++ b/internal/web/actions/default/clusters/cluster/node/update.go @@ -6,6 +6,7 @@ import ( "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/nodes/ipAddresses/ipaddressutils" + "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" "github.com/iwind/TeaGo/actions" @@ -46,7 +47,10 @@ func (this *UpdateAction) RunGet(params struct { } // 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 { this.ErrorPage(err) return @@ -340,14 +344,17 @@ func (this *UpdateAction) RunPost(params struct { } // 禁用老的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 { this.ErrorPage(err) return } // 添加新的IP地址 - err = ipaddressutils.UpdateNodeIPAddresses(this.Parent(), params.NodeId, params.IPAddressesJSON) + err = ipaddressutils.UpdateNodeIPAddresses(this.Parent(), params.NodeId, nodeconfigs.NodeRoleNode, params.IPAddressesJSON) if err != nil { this.ErrorPage(err) return diff --git a/internal/web/actions/default/clusters/index.go b/internal/web/actions/default/clusters/index.go index e2bd9c15..3b4a6823 100644 --- a/internal/web/actions/default/clusters/index.go +++ b/internal/web/actions/default/clusters/index.go @@ -186,7 +186,10 @@ func (this *IndexAction) searchNodes(keyword string) { } // 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 { this.ErrorPage(err) return diff --git a/internal/web/actions/default/nodes/delete.go b/internal/web/actions/default/nodes/delete.go index 935d6571..b18d1265 100644 --- a/internal/web/actions/default/nodes/delete.go +++ b/internal/web/actions/default/nodes/delete.go @@ -1,7 +1,6 @@ package nodes import ( - "github.com/TeaOSLab/EdgeAdmin/internal/oplogs" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" ) @@ -13,14 +12,14 @@ type DeleteAction struct { func (this *DeleteAction) RunPost(params struct { NodeId int64 }) { + // 创建日志 + defer this.CreateLogInfo("删除节点", params.NodeId) + _, err := this.RPC().NodeRPC().DeleteNode(this.AdminContext(), &pb.DeleteNodeRequest{NodeId: params.NodeId}) if err != nil { this.ErrorPage(err) return } - // 创建日志 - defer this.CreateLog(oplogs.LevelInfo, "删除节点", params.NodeId) - this.Success() } diff --git a/internal/web/actions/default/nodes/ipAddresses/ipaddressutils/utils.go b/internal/web/actions/default/nodes/ipAddresses/ipaddressutils/utils.go index e963fd40..1c4f644f 100644 --- a/internal/web/actions/default/nodes/ipAddresses/ipaddressutils/utils.go +++ b/internal/web/actions/default/nodes/ipAddresses/ipaddressutils/utils.go @@ -3,12 +3,13 @@ package ipaddressutils import ( "encoding/json" "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" ) -// 保存一组IP地址 -func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, ipAddressesJSON []byte) error { +// UpdateNodeIPAddresses 保存一组IP地址 +func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, role nodeconfigs.NodeRole, ipAddressesJSON []byte) error { addresses := []maps.Map{} err := json.Unmarshal(ipAddressesJSON, &addresses) if err != nil { @@ -29,6 +30,7 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, } else { _, err = parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{ NodeId: nodeId, + Role: role, Name: addr.GetString("name"), Ip: addr.GetString("ip"), CanAccess: addr.GetBool("canAccess"), diff --git a/internal/web/actions/default/ns/clusters/cluster/createNode.go b/internal/web/actions/default/ns/clusters/cluster/createNode.go new file mode 100644 index 00000000..8ce1ff37 --- /dev/null +++ b/internal/web/actions/default/ns/clusters/cluster/createNode.go @@ -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() +} diff --git a/internal/web/actions/default/ns/clusters/cluster/deleteNode.go b/internal/web/actions/default/ns/clusters/cluster/deleteNode.go new file mode 100644 index 00000000..a98a9d18 --- /dev/null +++ b/internal/web/actions/default/ns/clusters/cluster/deleteNode.go @@ -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() +} diff --git a/internal/web/actions/default/ns/clusters/cluster/index.go b/internal/web/actions/default/ns/clusters/cluster/index.go index c9fcf0f5..105c41a0 100644 --- a/internal/web/actions/default/ns/clusters/cluster/index.go +++ b/internal/web/actions/default/ns/clusters/cluster/index.go @@ -2,16 +2,138 @@ 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 { actionutils.ParentAction } 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() } diff --git a/internal/web/actions/default/ns/clusters/cluster/init.go b/internal/web/actions/default/ns/clusters/cluster/init.go index ab9a0f0e..7f93c783 100644 --- a/internal/web/actions/default/ns/clusters/cluster/init.go +++ b/internal/web/actions/default/ns/clusters/cluster/init.go @@ -2,6 +2,7 @@ package cluster import ( "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/helpers" "github.com/iwind/TeaGo" @@ -17,6 +18,18 @@ func init() { Prefix("/ns/clusters/cluster"). Get("", new(IndexAction)). 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() }) } diff --git a/internal/web/actions/default/ns/clusters/cluster/node/index.go b/internal/web/actions/default/ns/clusters/cluster/node/index.go new file mode 100644 index 00000000..88f9cae3 --- /dev/null +++ b/internal/web/actions/default/ns/clusters/cluster/node/index.go @@ -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() +} diff --git a/internal/web/actions/default/ns/clusters/cluster/node/install.go b/internal/web/actions/default/ns/clusters/cluster/node/install.go new file mode 100644 index 00000000..ce23c47d --- /dev/null +++ b/internal/web/actions/default/ns/clusters/cluster/node/install.go @@ -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() +} diff --git a/internal/web/actions/default/ns/clusters/cluster/node/logs.go b/internal/web/actions/default/ns/clusters/cluster/node/logs.go new file mode 100644 index 00000000..ddf0e9cb --- /dev/null +++ b/internal/web/actions/default/ns/clusters/cluster/node/logs.go @@ -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() +} diff --git a/internal/web/actions/default/ns/clusters/cluster/node/status.go b/internal/web/actions/default/ns/clusters/cluster/node/status.go new file mode 100644 index 00000000..869e5d2d --- /dev/null +++ b/internal/web/actions/default/ns/clusters/cluster/node/status.go @@ -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() +} diff --git a/internal/web/actions/default/ns/clusters/cluster/node/update.go b/internal/web/actions/default/ns/clusters/cluster/node/update.go new file mode 100644 index 00000000..a87ff565 --- /dev/null +++ b/internal/web/actions/default/ns/clusters/cluster/node/update.go @@ -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() +} diff --git a/internal/web/actions/default/ns/clusters/cluster/node/updateInstallStatus.go b/internal/web/actions/default/ns/clusters/cluster/node/updateInstallStatus.go new file mode 100644 index 00000000..27850d00 --- /dev/null +++ b/internal/web/actions/default/ns/clusters/cluster/node/updateInstallStatus.go @@ -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() +} diff --git a/internal/web/actions/default/ns/clusters/index.go b/internal/web/actions/default/ns/clusters/index.go index 1cd6e9a4..57cb2016 100644 --- a/internal/web/actions/default/ns/clusters/index.go +++ b/internal/web/actions/default/ns/clusters/index.go @@ -4,8 +4,10 @@ package clusters import ( "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/configutils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/maps" + "github.com/iwind/TeaGo/types" ) type IndexAction struct { @@ -36,10 +38,37 @@ func (this *IndexAction) RunGet(params struct{}) { } clusterMaps := []maps.Map{} 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{ - "id": cluster.Id, - "name": cluster.Name, - "isOn": cluster.IsOn, + "id": cluster.Id, + "name": cluster.Name, + "isOn": cluster.IsOn, + "countAllNodes": countNodesResp.Count, + "countActiveNodes": countActiveNodesResp.Count, + "countUpgradeNodes": countUpgradeNodesResp.Count, }) } this.Data["clusters"] = clusterMaps diff --git a/web/views/@default/clusters/cluster/node/node.css b/web/views/@default/clusters/cluster/node/index.css similarity index 58% rename from web/views/@default/clusters/cluster/node/node.css rename to web/views/@default/clusters/cluster/node/index.css index e395e4ca..1b23bc85 100644 --- a/web/views/@default/clusters/cluster/node/node.css +++ b/web/views/@default/clusters/cluster/node/index.css @@ -1,4 +1,4 @@ a.underline { border-bottom: 1px #db2828 dashed; } -/*# sourceMappingURL=node.css.map */ \ No newline at end of file +/*# sourceMappingURL=index.css.map */ \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/index.css.map b/web/views/@default/clusters/cluster/node/index.css.map new file mode 100644 index 00000000..31fa6632 --- /dev/null +++ b/web/views/@default/clusters/cluster/node/index.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"index.css"} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/node.html b/web/views/@default/clusters/cluster/node/index.html similarity index 100% rename from web/views/@default/clusters/cluster/node/node.html rename to web/views/@default/clusters/cluster/node/index.html diff --git a/web/views/@default/clusters/cluster/node/node.js b/web/views/@default/clusters/cluster/node/index.js similarity index 100% rename from web/views/@default/clusters/cluster/node/node.js rename to web/views/@default/clusters/cluster/node/index.js diff --git a/web/views/@default/clusters/cluster/node/node.less b/web/views/@default/clusters/cluster/node/index.less similarity index 100% rename from web/views/@default/clusters/cluster/node/node.less rename to web/views/@default/clusters/cluster/node/index.less diff --git a/web/views/@default/clusters/cluster/node/node.css.map b/web/views/@default/clusters/cluster/node/node.css.map deleted file mode 100644 index db4129ac..00000000 --- a/web/views/@default/clusters/cluster/node/node.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["node.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"node.css"} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/update.js b/web/views/@default/clusters/cluster/node/update.js index 3f336d93..4a336bd6 100644 --- a/web/views/@default/clusters/cluster/node/update.js +++ b/web/views/@default/clusters/cluster/node/update.js @@ -9,29 +9,6 @@ Tea.context(function () { // IP地址相关 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; diff --git a/web/views/@default/clusters/index.html b/web/views/@default/clusters/index.html index a982844e..44334896 100644 --- a/web/views/@default/clusters/index.html +++ b/web/views/@default/clusters/index.html @@ -33,8 +33,8 @@ 集群名称 - 节点数量 - 在线节点数量 + 节点数 + 在线节点数 DNS域名 操作 diff --git a/web/views/@default/ns/clusters/cluster/@menu.html b/web/views/@default/ns/clusters/cluster/@menu.html new file mode 100644 index 00000000..490b7f90 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/@menu.html @@ -0,0 +1,6 @@ + + 节点列表 + 创建节点 + + \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/createNode.css b/web/views/@default/ns/clusters/cluster/createNode.css new file mode 100644 index 00000000..e2dadd67 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/createNode.css @@ -0,0 +1,7 @@ +.left-box { + top: 10em; +} +.right-box { + top: 10em; +} +/*# sourceMappingURL=createNode.css.map */ \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/createNode.css.map b/web/views/@default/ns/clusters/cluster/createNode.css.map new file mode 100644 index 00000000..fd804f17 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/createNode.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["createNode.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"createNode.css"} \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/createNode.html b/web/views/@default/ns/clusters/cluster/createNode.html new file mode 100644 index 00000000..74ab654e --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/createNode.html @@ -0,0 +1,27 @@ +{$layout} +{$template "../menu"} + +
+ + + + + + + + + + + + + +
节点名称 * + +
IP地址 * + +

用于访问节点和域名解析等。

+
+ +
+ +
\ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/createNode.js b/web/views/@default/ns/clusters/cluster/createNode.js new file mode 100644 index 00000000..decbfe9a --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/createNode.js @@ -0,0 +1,3 @@ +Tea.context(function () { + this.success = NotifySuccess("保存成功", "/ns/clusters/cluster?clusterId=" + this.clusterId); +}); \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/createNode.less b/web/views/@default/ns/clusters/cluster/createNode.less new file mode 100644 index 00000000..f54837f3 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/createNode.less @@ -0,0 +1,7 @@ +.left-box { + top: 10em; +} + +.right-box { + top: 10em; +} \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/index.html b/web/views/@default/ns/clusters/cluster/index.html index 8deefc83..52b1ea0d 100644 --- a/web/views/@default/ns/clusters/cluster/index.html +++ b/web/views/@default/ns/clusters/cluster/index.html @@ -1 +1,102 @@ {$layout} +{$template "menu"} + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ +
+
+

暂时还没有节点。

+
+ + + + + + + + + + + + + + + + + + + + + +
节点名称IPCPU内存状态操作
{{node.name}} + - +
+
+
{{addr.ip}} + ({{addr.name}},不可访问 + (不可访问) +
+
+
+
+ {{node.status.cpuUsageText}} + - + + {{node.status.memUsageText}} + - + +
+ 健康问题下线 +
+ [上线] +
+
+
+ +
+
+
+ 同步中 + 运行中 +
+ 已断开 + 未连接 +
+
+ 安装中 + 安装出错 + 未安装 +
+
+ 详情   删除 +
+ + +
\ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/index.js b/web/views/@default/ns/clusters/cluster/index.js new file mode 100644 index 00000000..046170c8 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/index.js @@ -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() + }) + } +}) \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/@node_menu.html b/web/views/@default/ns/clusters/cluster/node/@node_menu.html new file mode 100644 index 00000000..ed545e57 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/@node_menu.html @@ -0,0 +1,10 @@ + + 节点列表 + | + 节点详情 + + 运行日志 + 修改设置 + 安装节点 + \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/index.css b/web/views/@default/ns/clusters/cluster/node/index.css new file mode 100644 index 00000000..1b23bc85 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/index.css @@ -0,0 +1,4 @@ +a.underline { + border-bottom: 1px #db2828 dashed; +} +/*# sourceMappingURL=index.css.map */ \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/index.css.map b/web/views/@default/ns/clusters/cluster/node/index.css.map new file mode 100644 index 00000000..31fa6632 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/index.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"index.css"} \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/index.html b/web/views/@default/ns/clusters/cluster/node/index.html new file mode 100644 index 00000000..07212787 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/index.html @@ -0,0 +1,111 @@ +{$layout} + +{$template "node_menu"} + +

节点详情

+ + + + + + + + + + + + + +
节点名称{{node.name}}
状态
IP地址 +
+
+
+ {{address.ip}} + ({{address.name}},不可访问 + (不可访问) +
+
+
+
+ 暂时还没有填写IP地址。 +
+
+
+ +

运行状态

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
是否在运行 +
+ 运行中   + [通过SSH停止] + [停止中...] +
+
+ 已断开   + [通过SSH启动] + [启动中...] + 去安装> +
+
CPU用量{{node.status.cpuUsageText}}   ({{node.status.cpuPhysicalCount}}核心/{{node.status.cpuLogicalCount}}线程)
内存用量{{node.status.memUsageText}}
连接数{{node.status.connectionCount}}
负载{{node.status.load1m}}   {{node.status.load5m}}   {{node.status.load15m}}  
缓存用量 + 磁盘:{{node.status.cacheTotalDiskSize}}   内存:{{node.status.cacheTotalMemorySize}} +
版本v{{node.status.buildVersion}} +   发现新版本v{{newVersion}} » +
+

每隔30秒钟更新一次运行状态。

+ +
+

安装信息

+ + + + + + + + + + + + + + + + + +
节点ID(id){{node.uniqueId}}
密钥(secret){{node.secret}}
安装目录 +
使用集群设置({{node.cluster.installDir}})
+ {{node.installDir}} +
是否已安装 + 已安装 + 未安装 +
\ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/index.js b/web/views/@default/ns/clusters/cluster/node/index.js new file mode 100644 index 00000000..8bbb2c8f --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/index.js @@ -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 + }) + } +}) \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/index.less b/web/views/@default/ns/clusters/cluster/node/index.less new file mode 100644 index 00000000..48f0e935 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/index.less @@ -0,0 +1,3 @@ +a.underline { + border-bottom: 1px #db2828 dashed; +} \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/install.css b/web/views/@default/ns/clusters/cluster/node/install.css new file mode 100644 index 00000000..9e8d1e9b --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/install.css @@ -0,0 +1,7 @@ +.installing-box { + line-height: 1.8; +} +.installing-box .blue { + color: #2185d0; +} +/*# sourceMappingURL=install.css.map */ \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/install.css.map b/web/views/@default/ns/clusters/cluster/node/install.css.map new file mode 100644 index 00000000..5e4d0894 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/install.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["install.less"],"names":[],"mappings":"AAAA;EACC,gBAAA;;AADD,eAGC;EACC,cAAA","file":"install.css"} \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/install.html b/web/views/@default/ns/clusters/cluster/node/install.html new file mode 100644 index 00000000..bb1d24a7 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/install.html @@ -0,0 +1,54 @@ +{$layout} +{$template "node_menu"} +{$template "/code_editor"} + + +
+
当前节点为已安装状态。
+ [修改为未安装状态] +
+ + +
+

方法1:通过SSH自动安装

+ +
+
安装中...
+
+ 已安装成功 + 安装过程中发生错误:{{installStatus.error}} +
+
+
+ +
+
+ +
+ +

方法2:手动安装

+ + + + + + + + + +
配置文件(configs/api.yaml)
+ [下载]
+ rpc: + endpoints: [ {{apiEndpoints}} ] +nodeId: "{{node.uniqueId}}" +secret: "{{node.secret}}" +
安装目录 +
使用集群设置({{node.cluster.installDir}}) +
+ {{node.installDir}} +
+ + [修改为已安装状态] +
\ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/install.js b/web/views/@default/ns/clusters/cluster/node/install.js new file mode 100644 index 00000000..cfd71772 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/install.js @@ -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 + ",
现在修改API信息?", function () { + window.location = "/api" + }) + return + default: + shouldReload = true + //teaweb.warn("安装失败:" + errMsg) + } + }) + .done(function () { + this.$delay(function () { + this.reloadStatus(nodeId) + }, 1000) + }); + } +}) \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/install.less b/web/views/@default/ns/clusters/cluster/node/install.less new file mode 100644 index 00000000..cbe14160 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/install.less @@ -0,0 +1,7 @@ +.installing-box { + line-height: 1.8; + + .blue { + color: #2185d0; + } +} \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/logs.css b/web/views/@default/ns/clusters/cluster/node/logs.css new file mode 100644 index 00000000..dafa1562 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/logs.css @@ -0,0 +1,5 @@ +pre.log-box { + margin: 0; + padding: 0; +} +/*# sourceMappingURL=logs.css.map */ \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/logs.css.map b/web/views/@default/ns/clusters/cluster/node/logs.css.map new file mode 100644 index 00000000..6ae867b7 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/logs.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["logs.less"],"names":[],"mappings":"AAAA,GAAG;EACF,SAAA;EACA,UAAA","file":"logs.css"} \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/logs.html b/web/views/@default/ns/clusters/cluster/node/logs.html new file mode 100644 index 00000000..fb1afe08 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/logs.html @@ -0,0 +1,58 @@ +{$layout} +{$template "node_menu"} + +{$var "header"} + + + + + + +{$end} + +
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+ +

暂时还没有日志。

+ + + + + + + + + + +
+
[{{log.createdTime}}][{{log.createdTime}}][{{log.tag}}]{{log.description}}   共{{log.count}}条
+
+ +
\ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/logs.js b/web/views/@default/ns/clusters/cluster/node/logs.js new file mode 100644 index 00000000..29a90420 --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/logs.js @@ -0,0 +1,6 @@ +Tea.context(function () { + this.$delay(function () { + teaweb.datepicker("day-from-picker") + teaweb.datepicker("day-to-picker") + }) +}) \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/logs.less b/web/views/@default/ns/clusters/cluster/node/logs.less new file mode 100644 index 00000000..9accd63d --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/logs.less @@ -0,0 +1,4 @@ +pre.log-box { + margin: 0; + padding: 0; +} \ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/update.html b/web/views/@default/ns/clusters/cluster/node/update.html new file mode 100644 index 00000000..e8e9410b --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/update.html @@ -0,0 +1,47 @@ +{$layout} + +{$template "node_menu"} + +

修改节点

+
+ + + + + + + + + + + + + + + + + + + + + + + +
节点名称 * + +
IP地址 * + +

用于访问节点和域名解析等。

+
所属集群 + +
是否启用 +
+ + +
+

如果不启用此节点,此节点上的所有服务将不能访问。

+
+ +
\ No newline at end of file diff --git a/web/views/@default/ns/clusters/cluster/node/update.js b/web/views/@default/ns/clusters/cluster/node/update.js new file mode 100644 index 00000000..8e1adb9d --- /dev/null +++ b/web/views/@default/ns/clusters/cluster/node/update.js @@ -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); +}); \ No newline at end of file diff --git a/web/views/@default/ns/clusters/index.html b/web/views/@default/ns/clusters/index.html index 56515294..f6424602 100644 --- a/web/views/@default/ns/clusters/index.html +++ b/web/views/@default/ns/clusters/index.html @@ -7,12 +7,26 @@ 集群名称 + 节点数 + 在线节点数 集群状态 操作 {{cluster.name}} + + {{cluster.countAllNodes}} + - + +
+ 有节点需要升级 +
+ + + {{cluster.countActiveNodes}} + - +