From 86a5992e8a9780dccbb9e39ea5d30c156612ca28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A5=A5=E8=B6=85?= Date: Sat, 14 Aug 2021 18:06:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=8A=82=E7=82=B9=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/rpc/rpc_client.go | 4 + .../default/clusters/cluster/createNode.go | 69 +++++++ .../clusters/cluster/createNodeInstall.go | 76 ++++++++ .../actions/default/clusters/cluster/init.go | 3 +- .../default/clusters/cluster/node/install.go | 4 +- .../clusters/cluster/suggestLoginPorts.go | 36 ++++ .../js/components/grant/grant-selector.js | 25 ++- .../node/node-login-suggest-ports.js | 60 ++++++ .../@default/clusters/cluster/createNode.css | 6 + .../clusters/cluster/createNode.css.map | 2 +- .../@default/clusters/cluster/createNode.html | 100 +++++++++- .../@default/clusters/cluster/createNode.js | 174 +++++++++++++++++- .../@default/clusters/cluster/createNode.less | 8 + 13 files changed, 550 insertions(+), 17 deletions(-) create mode 100644 internal/web/actions/default/clusters/cluster/createNodeInstall.go create mode 100644 internal/web/actions/default/clusters/cluster/suggestLoginPorts.go create mode 100644 web/public/js/components/node/node-login-suggest-ports.js diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go index 50c9144b..c3672f6e 100644 --- a/internal/rpc/rpc_client.go +++ b/internal/rpc/rpc_client.go @@ -71,6 +71,10 @@ func (this *RPCClient) NodeGrantRPC() pb.NodeGrantServiceClient { return pb.NewNodeGrantServiceClient(this.pickConn()) } +func (this *RPCClient) NodeLoginRPC() pb.NodeLoginServiceClient { + return pb.NewNodeLoginServiceClient(this.pickConn()) +} + func (this *RPCClient) NodeClusterRPC() pb.NodeClusterServiceClient { return pb.NewNodeClusterServiceClient(this.pickConn()) } diff --git a/internal/web/actions/default/clusters/cluster/createNode.go b/internal/web/actions/default/clusters/cluster/createNode.go index 8d844e14..ebe84ed9 100644 --- a/internal/web/actions/default/clusters/cluster/createNode.go +++ b/internal/web/actions/default/clusters/cluster/createNode.go @@ -4,11 +4,13 @@ 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/clusters/grants/grantutils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/actions" "github.com/iwind/TeaGo/maps" "strconv" + "strings" ) // CreateNodeAction 创建节点 @@ -67,6 +69,22 @@ func (this *CreateNodeAction) RunGet(params struct { } this.Data["dnsRoutes"] = dnsRouteMaps + // 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.Show() } @@ -174,5 +192,56 @@ func (this *CreateNodeAction) RunPost(params struct { // 创建日志 defer this.CreateLog(oplogs.LevelInfo, "创建节点 %d", nodeId) + // 响应数据 + this.Data["nodeId"] = nodeId + nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: nodeId}) + if err != nil { + this.ErrorPage(err) + return + } + if nodeResp.Node != nil { + var addresses = []string{} + for _, addrMap := range ipAddresses { + addresses = append(addresses, addrMap.GetString("ip")) + } + + var grantMap maps.Map = nil + grantId := params.GrantId + if grantId > 0 { + grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId}) + if err != nil { + this.ErrorPage(err) + return + } + if grantResp.NodeGrant != nil && grantResp.NodeGrant.Id > 0 { + grantMap = maps.Map{ + "id": grantResp.NodeGrant.Id, + "name": grantResp.NodeGrant.Name, + "method": grantResp.NodeGrant.Method, + "methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method), + } + } + } + + this.Data["node"] = maps.Map{ + "id": nodeResp.Node.Id, + "name": nodeResp.Node.Name, + "uniqueId": nodeResp.Node.UniqueId, + "secret": nodeResp.Node.Secret, + "addresses": addresses, + "login": maps.Map{ + "id": 0, + "name": "SSH", + "type": "ssh", + "params": maps.Map{ + "grantId": params.GrantId, + "host": params.SshHost, + "port": params.SshPort, + }, + }, + "grant": grantMap, + } + } + this.Success() } diff --git a/internal/web/actions/default/clusters/cluster/createNodeInstall.go b/internal/web/actions/default/clusters/cluster/createNodeInstall.go new file mode 100644 index 00000000..16cf73c5 --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/createNodeInstall.go @@ -0,0 +1,76 @@ +// 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" + "github.com/iwind/TeaGo/actions" + "github.com/iwind/TeaGo/maps" +) + +type CreateNodeInstallAction struct { + actionutils.ParentAction +} + +func (this *CreateNodeInstallAction) RunPost(params struct { + NodeId int64 + SshHost string + SshPort int + GrantId int64 + + Must *actions.Must +}) { + defer this.CreateLogInfo("安装节点 %d", params.NodeId) + + params.Must. + Field("sshHost2", params.SshHost). + Require("请填写SSH主机地址"). + Field("sshPort2", params.SshPort). + Gt(0, "请填写SSH主机端口"). + Lt(65535, "SSH主机端口需要小于65535"). + Field("grantId", params.GrantId). + Gt(0, "请选择SSH登录认证") + + // 查询login + nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId}) + if err != nil { + this.ErrorPage(err) + return + } + var node = nodeResp.Node + if node == nil { + this.Fail("找不到要修改的节点") + } + var loginId int64 + if node.NodeLogin != nil { + loginId = node.NodeLogin.Id + } + + // 修改节点信息 + _, err = this.RPC().NodeRPC().UpdateNodeLogin(this.AdminContext(), &pb.UpdateNodeLoginRequest{ + NodeId: params.NodeId, + NodeLogin: &pb.NodeLogin{ + Id: loginId, + Name: "SSH", + Type: "ssh", + Params: maps.Map{ + "grantId": params.GrantId, + "host": params.SshHost, + "port": params.SshPort, + }.AsJSON(), + }, + }) + if err != nil { + this.ErrorPage(err) + return + } + + // 开始安装 + _, err = this.RPC().NodeRPC().InstallNode(this.AdminContext(), &pb.InstallNodeRequest{NodeId: params.NodeId}) + if err != nil { + this.Fail("安装失败:" + err.Error()) + } + + this.Success() +} diff --git a/internal/web/actions/default/clusters/cluster/init.go b/internal/web/actions/default/clusters/cluster/init.go index 6499fb57..34fce311 100644 --- a/internal/web/actions/default/clusters/cluster/init.go +++ b/internal/web/actions/default/clusters/cluster/init.go @@ -27,9 +27,11 @@ func init() { Post("/upgradeStatus", new(UpgradeStatusAction)). GetPost("/delete", new(DeleteAction)). GetPost("/createNode", new(CreateNodeAction)). + Post("/createNodeInstall", new(CreateNodeInstallAction)). GetPost("/createBatch", new(CreateBatchAction)). GetPost("/updateNodeSSH", new(UpdateNodeSSHAction)). GetPost("/installManual", new(InstallManualAction)). + Post("/suggestLoginPorts", new(SuggestLoginPortsAction)). // 节点相关 Prefix("/clusters/cluster/node"). @@ -58,7 +60,6 @@ func init() { // 看板相关 Prefix("/clusters/cluster/boards"). Get("", new(boards.IndexAction)). - EndAll() }) } diff --git a/internal/web/actions/default/clusters/cluster/node/install.go b/internal/web/actions/default/clusters/cluster/node/install.go index 2d1b6920..78936350 100644 --- a/internal/web/actions/default/clusters/cluster/node/install.go +++ b/internal/web/actions/default/clusters/cluster/node/install.go @@ -9,7 +9,7 @@ import ( "strings" ) -// 安装节点 +// InstallAction 安装节点 type InstallAction struct { actionutils.ParentAction } @@ -97,7 +97,7 @@ func (this *InstallAction) RunGet(params struct { this.Show() } -// 开始安装 +// RunPost 开始安装 func (this *InstallAction) RunPost(params struct { NodeId int64 diff --git a/internal/web/actions/default/clusters/cluster/suggestLoginPorts.go b/internal/web/actions/default/clusters/cluster/suggestLoginPorts.go new file mode 100644 index 00000000..65cac27a --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/suggestLoginPorts.go @@ -0,0 +1,36 @@ +// 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 SuggestLoginPortsAction struct { + actionutils.ParentAction +} + +func (this *SuggestLoginPortsAction) RunPost(params struct { + Host string +}) { + portsResp, err := this.RPC().NodeLoginRPC().FindNodeLoginSuggestPorts(this.AdminContext(), &pb.FindNodeLoginSuggestPortsRequest{Host: params.Host}) + if err != nil { + this.ErrorPage(err) + return + } + + if len(portsResp.Ports) == 0 { + this.Data["ports"] = []int32{} + } else { + this.Data["ports"] = portsResp.Ports + } + + if len(portsResp.AvailablePorts) == 0 { + this.Data["availablePorts"] = []int32{} + } else { + this.Data["availablePorts"] = portsResp.AvailablePorts + } + + this.Success() +} diff --git a/web/public/js/components/grant/grant-selector.js b/web/public/js/components/grant/grant-selector.js index a3019667..746483da 100644 --- a/web/public/js/components/grant/grant-selector.js +++ b/web/public/js/components/grant/grant-selector.js @@ -16,21 +16,24 @@ Vue.component("grant-selector", { if (that.grantId > 0) { that.grant = resp.data.grant; } + that.notifyUpdate() } - }); + }) }, // 创建授权 create: function () { + let that = this teaweb.popup("/clusters/grants/createPopup", { height: "26em", callback: (resp) => { - this.grantId = resp.data.grant.id; - if (this.grantId > 0) { - this.grant = resp.data.grant; + that.grantId = resp.data.grant.id; + if (that.grantId > 0) { + that.grant = resp.data.grant; } + that.notifyUpdate() } - }); + }) }, // 修改授权 @@ -39,18 +42,24 @@ Vue.component("grant-selector", { window.location.reload(); return; } + let that = this teaweb.popup("/clusters/grants/updatePopup?grantId=" + this.grant.id, { height: "26em", callback: (resp) => { - this.grant = resp.data.grant; + that.grant = resp.data.grant + that.notifyUpdate() } }) }, // 删除已选择授权 remove: function () { - this.grant = null; - this.grantId = 0; + this.grant = null + this.grantId = 0 + this.notifyUpdate() + }, + notifyUpdate: function () { + this.$emit("change", this.grant) } }, template: `
diff --git a/web/public/js/components/node/node-login-suggest-ports.js b/web/public/js/components/node/node-login-suggest-ports.js new file mode 100644 index 00000000..f571b2be --- /dev/null +++ b/web/public/js/components/node/node-login-suggest-ports.js @@ -0,0 +1,60 @@ +// 节点登录推荐端口 +Vue.component("node-login-suggest-ports", { + data: function () { + return { + ports: [], + availablePorts: [], + autoSelected: false, + isLoading: false + } + }, + methods: { + reload: function (host) { + let that = this + this.autoSelected = false + this.isLoading = true + Tea.action("/clusters/cluster/suggestLoginPorts") + .params({ + host: host + }) + .success(function (resp) { + if (resp.data.availablePorts != null) { + that.availablePorts = resp.data.availablePorts + if (that.availablePorts.length > 0) { + that.autoSelectPort(that.availablePorts[0]) + that.autoSelected = true + } + } + if (resp.data.ports != null) { + that.ports = resp.data.ports + if (that.ports.length > 0 && !that.autoSelected) { + that.autoSelectPort(that.ports[0]) + that.autoSelected = true + } + } + }) + .done(function () { + that.isLoading = false + }) + .post() + }, + selectPort: function (port) { + this.$emit("select", port) + }, + autoSelectPort: function (port) { + this.$emit("auto-select", port) + } + }, + template: ` + 正在检查端口... + + 推荐端口:{{port}} +     + + + 常用端口:{{port}} + + 常用端口有22等。 + (可以点击要使用的端口) +` +}) \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/createNode.css b/web/views/@default/clusters/cluster/createNode.css index e2dadd67..3fca0942 100644 --- a/web/views/@default/clusters/cluster/createNode.css +++ b/web/views/@default/clusters/cluster/createNode.css @@ -4,4 +4,10 @@ .right-box { top: 10em; } +.row { + line-height: 4; +} +.step.active { + font-weight: bold; +} /*# sourceMappingURL=createNode.css.map */ \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/createNode.css.map b/web/views/@default/clusters/cluster/createNode.css.map index fd804f17..5b11676f 100644 --- a/web/views/@default/clusters/cluster/createNode.css.map +++ b/web/views/@default/clusters/cluster/createNode.css.map @@ -1 +1 @@ -{"version":3,"sources":["createNode.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"createNode.css"} \ No newline at end of file +{"version":3,"sources":["createNode.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA;;AAGD;EACC,cAAA;;AAGD,KAAK;EACJ,iBAAA","file":"createNode.css"} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/createNode.html b/web/views/@default/clusters/cluster/createNode.html index c670c72d..e1f1d1aa 100644 --- a/web/views/@default/clusters/cluster/createNode.html +++ b/web/views/@default/clusters/cluster/createNode.html @@ -1,9 +1,23 @@ {$layout} {$template "/clusters/cluster/menu"} {$template "/left_menu"} +{$template "/code_editor"}
-
+
+
+
1. 填写节点信息
+
+
+
2. 安装节点
+
+
+
3. 完成
+
+
+ + + @@ -51,7 +65,7 @@ @@ -69,6 +83,86 @@
SSH主机地址 -

比如192.168.1.100

+

比如192.168.1.100,用于远程安装节点应用程序。

- + 下一步
+ + +
+ + + +
+
+ + + + + + + + + + + + + +
SSH主机地址 * + +
+ {{addr}} +
+

比如192.168.1.100,用于远程安装节点应用程序。

+
SSH主机端口 * + +

+
SSH登录认证 * + +
+ + +
+
安装中...
+
+ 已安装成功 + 安装过程中发生错误:{{installStatus.error}} +
+
+ + + + 跳过安装 +
+
+ + +
+
在边缘节点安装目录下,复制configs/api.template.yamlconfigs/api.yaml,然后修改文件里面的内容为以下内容:
+ rpc: + endpoints: [ {{apiEndpoints}} ] +nodeId: "{{node.uniqueId}}" +secret: "{{node.secret}}" +
然后再使用bin/edge-node start命令启动节点。
+ +
+
+ + +
+
+
"{{node.name}}"节点已被创建并安装成功。
+
"{{node.name}}"节点已创建成功。
+ + +
+
\ No newline at end of file diff --git a/web/views/@default/clusters/cluster/createNode.js b/web/views/@default/clusters/cluster/createNode.js index 497b92c3..0996a4bb 100644 --- a/web/views/@default/clusters/cluster/createNode.js +++ b/web/views/@default/clusters/cluster/createNode.js @@ -1,3 +1,173 @@ Tea.context(function () { - this.success = NotifySuccess("保存成功", "/clusters/cluster/nodes?clusterId=" + this.clusterId); -}); \ No newline at end of file + this.nodeId = 0 + this.node = {} + this.sshHost = "" + this.sshPort = "" + this.grantId = 0 + this.step = "info" + + this.success = function (resp) { + this.node = resp.data.node + this.nodeId = this.node.id + this.sshHost = this.node.login.params.host + if (this.node.login.params.port > 0) { + this.sshPort = this.node.login.params.port + } + if (this.node.addresses.length > 0) { + this.sshHost = this.node.addresses[0] + } + this.step = "install" + this.$delay(function () { + this.$refs.installSSHHostRef.focus() + this.$refs.nodeLoginSuggestPortsRef.reload(this.sshHost) + }) + } + + /** + * 安装 + */ + this.isInstalled = false + this.installMethod = "remote" // remote | manual + this.isInstalling = false + + this.switchInstallMethod = function (method) { + this.installMethod = method + } + + this.selectSSHHost = function (host) { + this.sshHost = host + this.changeSSHHost() + } + + this.changeSSHHost = function () { + if (this.$refs.nodeLoginSuggestPortsRef != null) { + this.$refs.nodeLoginSuggestPortsRef.reload(this.sshHost) + } + } + + this.selectLoginPort = function (port) { + this.sshPort = port + } + + this.autoSelectLoginPort = function (port) { + if (this.sshPort == null || this.sshPort <= 0) { + this.sshPort = port + } + } + + this.install = function () { + if (this.node.grant != null) { + this.grantId = this.node.grant.id + } + + this.isInstalling = true + this.$post(".createNodeInstall") + .params({ + nodeId: this.node.id, + sshHost: this.sshHost, + sshPort: this.sshPort, + grantId: this.grantId + }) + .timeout(30) + .success(function () { + this.$delay(function () { + this.isInstalling = true + this.reloadStatus(this.node.id) + }) + }) + .done(function () { + this.isInstalling = false + }) + } + + this.changeGrant = function (grant) { + if (grant != null) { + this.grantId = grant.id + } else { + this.grantId = 0 + } + } + + // 刷新状态 + this.installStatus = null + this.reloadStatus = function (nodeId) { + let that = this + + this.$post("/clusters/cluster/node/status") + .params({ + nodeId: nodeId + }) + .success(function (resp) { + this.installStatus = resp.data.installStatus + this.node.isInstalled = resp.data.isInstalled + if (this.node.isInstalled) { + this.isInstalling = false + this.isInstalled = true + this.finish() + } + + if (!this.isInstalling) { + return + } + + let nodeId = this.node.id + let errMsg = this.installStatus.error + + if (this.installStatus.errorCode.length > 0 || errMsg.length > 0) { + this.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("/clusters/cluster/updateNodeSSH?nodeId=" + nodeId, { + height: "30em", + callback: function () { + that.install() + } + }) + }) + return + case "SSH_LOGIN_FAILED": + teaweb.warn("SSH登录失败,请检查设置") + 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) + }); + } + + /** + * 完成 + */ + this.finish = function () { + this.step = "finish" + } + + this.createNext = function () { + teaweb.reload() + } +}) \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/createNode.less b/web/views/@default/clusters/cluster/createNode.less index f54837f3..721683e5 100644 --- a/web/views/@default/clusters/cluster/createNode.less +++ b/web/views/@default/clusters/cluster/createNode.less @@ -4,4 +4,12 @@ .right-box { top: 10em; +} + +.row { + line-height: 4; +} + +.step.active { + font-weight: bold; } \ No newline at end of file