Tea.context(function () { let that = this // 计算总待升级数 this.totalUpgradeCount = 0 this.modules.forEach(function (mod) { mod.expanded = true that.totalUpgradeCount += mod.count if (mod.clusters != null) { mod.clusters.forEach(function (cluster) { cluster.expanded = true if (cluster.nodes != null) { cluster.nodes.forEach(function (node) { node.isUpgrading = false node.isChecked = false }) } }) } }) // 正在升级的节点ID集合(按模块) this.upgradingNodeIds = { "node": [], "dns": [], "httpdns": [] } // 启动状态轮询 this.$delay(function () { this.pollStatus() }) /** * 获取节点详情页URL */ this.nodeDetailURL = function (moduleCode, clusterId, nodeId) { switch (moduleCode) { case "node": return "/clusters/cluster/node?clusterId=" + clusterId + "&nodeId=" + nodeId case "dns": return "/ns/clusters/cluster/node?clusterId=" + clusterId + "&nodeId=" + nodeId case "httpdns": return "/httpdns/clusters/cluster/node?clusterId=" + clusterId + "&nodeId=" + nodeId default: return "#" } } /** * 判断节点升级是否已完成(成功) */ this.isNodeUpgradeFinished = function (node) { return node.installStatus != null && node.installStatus.isFinished && node.installStatus.isOk } /** * 全选/取消全选 */ this.toggleCheckAll = function (cluster) { if (cluster.nodes == null) return let allChecked = that.isAllChecked(cluster) cluster.nodes.forEach(function (node) { if (!that.isNodeUpgradeFinished(node)) { node.isChecked = !allChecked } }) } /** * 是否全部选中 */ this.isAllChecked = function (cluster) { if (cluster.nodes == null || cluster.nodes.length == 0) return false let checkableNodes = cluster.nodes.filter(function (node) { return !that.isNodeUpgradeFinished(node) }) if (checkableNodes.length == 0) return false return checkableNodes.every(function (node) { return node.isChecked }) } /** * 计算集群内选中的节点数 */ this.countCheckedNodesInCluster = function (cluster) { if (cluster.nodes == null) return 0 return cluster.nodes.filter(function (node) { return node.isChecked }).length } /** * 全部升级 */ this.upgradeAll = function () { var vue = this teaweb.confirm("确定要升级所有模块的所有待升级节点吗?", function () { vue.$post("/settings/upgrade/upgradeNode") .params({ module: "", scope: "all", clusterId: 0, nodeId: 0 }) .success(function () { // 标记所有节点为升级中 that.modules.forEach(function (mod) { if (mod.clusters != null) { mod.clusters.forEach(function (cluster) { if (cluster.nodes != null) { cluster.nodes.forEach(function (node) { node.installStatus = { isRunning: true, isFinished: false, isOk: false, error: "", errorCode: "" } node.isUpgrading = true that.trackNode(mod.code, node.id) }) } }) } }) }) .fail(function (resp) { teaweb.warn("升级请求失败:" + resp.message) }) }) } /** * 按模块升级 */ this.upgradeModule = function (moduleCode) { var vue = this teaweb.confirm("确定要升级该模块的所有待升级节点吗?", function () { vue.$post("/settings/upgrade/upgradeNode") .params({ module: moduleCode, scope: "module", clusterId: 0, nodeId: 0 }) .success(function () { that.markModuleUpgrading(moduleCode) }) .fail(function (resp) { teaweb.warn("升级请求失败:" + resp.message) }) }) } /** * 按集群升级 */ this.upgradeCluster = function (moduleCode, clusterId) { var vue = this teaweb.confirm("确定要升级该集群的所有待升级节点吗?", function () { vue.$post("/settings/upgrade/upgradeNode") .params({ module: moduleCode, scope: "cluster", clusterId: clusterId, nodeId: 0 }) .success(function () { that.markClusterUpgrading(moduleCode, clusterId) }) .fail(function (resp) { teaweb.warn("升级请求失败:" + resp.message) }) }) } /** * 批量升级集群内选中的节点 */ this.upgradeBatchInCluster = function (moduleCode, cluster) { if (cluster.nodes == null) return var checkedNodes = cluster.nodes.filter(function (node) { return node.isChecked }) if (checkedNodes.length == 0) return var vue = this teaweb.confirm("确定要批量升级选中的 " + checkedNodes.length + " 个节点吗?", function () { checkedNodes.forEach(function (node) { node.installStatus = { isRunning: true, isFinished: false, isOk: false, error: "", errorCode: "" } node.isUpgrading = true node.isChecked = false that.trackNode(moduleCode, node.id) vue.$post("/settings/upgrade/upgradeNode") .params({ module: moduleCode, scope: "node", clusterId: 0, nodeId: node.id }) }) }) } /** * 升级单个节点 */ this.upgradeNode = function (moduleCode, node) { var vue = this teaweb.confirm("确定要升级节点 \"" + node.name + "\" 吗?", function () { node.installStatus = { isRunning: true, isFinished: false, isOk: false, error: "", errorCode: "" } node.isUpgrading = true that.trackNode(moduleCode, node.id) vue.$post("/settings/upgrade/upgradeNode") .params({ module: moduleCode, scope: "node", clusterId: 0, nodeId: node.id }) .success(function () { }) .fail(function (resp) { node.isUpgrading = false teaweb.warn("升级请求失败:" + resp.message) }) }) } /** * 标记模块下所有节点为升级中 */ this.markModuleUpgrading = function (moduleCode) { that.modules.forEach(function (mod) { if (mod.code == moduleCode && mod.clusters != null) { mod.clusters.forEach(function (cluster) { if (cluster.nodes != null) { cluster.nodes.forEach(function (node) { node.installStatus = { isRunning: true, isFinished: false, isOk: false, error: "", errorCode: "" } node.isUpgrading = true that.trackNode(moduleCode, node.id) }) } }) } }) } /** * 标记集群下所有节点为升级中 */ this.markClusterUpgrading = function (moduleCode, clusterId) { that.modules.forEach(function (mod) { if (mod.code == moduleCode && mod.clusters != null) { mod.clusters.forEach(function (cluster) { if (cluster.id == clusterId && cluster.nodes != null) { cluster.nodes.forEach(function (node) { node.installStatus = { isRunning: true, isFinished: false, isOk: false, error: "", errorCode: "" } node.isUpgrading = true that.trackNode(moduleCode, node.id) }) } }) } }) } /** * 追踪节点升级状态 */ this.trackNode = function (moduleCode, nodeId) { if (that.upgradingNodeIds[moduleCode] == null) { that.upgradingNodeIds[moduleCode] = [] } if (that.upgradingNodeIds[moduleCode].indexOf(nodeId) < 0) { that.upgradingNodeIds[moduleCode].push(nodeId) } } /** * 状态轮询 */ this.pollStatus = function () { var vue = this // 检查是否有正在升级的节点 let hasUpgrading = false for (let key in that.upgradingNodeIds) { if (that.upgradingNodeIds[key].length > 0) { hasUpgrading = true break } } if (!hasUpgrading) { setTimeout(function () { that.pollStatus() }, 5000) return } vue.$post("/settings/upgrade/status") .params({ nodeIdsJSON: JSON.stringify(that.upgradingNodeIds) }) .success(function (resp) { let statuses = resp.data.statuses if (statuses == null) { return } // 更新各模块节点状态 for (let moduleCode in statuses) { let nodeStatuses = statuses[moduleCode] if (nodeStatuses == null) { continue } nodeStatuses.forEach(function (ns) { that.updateNodeStatus(moduleCode, ns.id, ns.installStatus) }) } }) .done(function () { setTimeout(function () { that.pollStatus() }, 3000) }) } /** * 更新节点安装状态 */ this.updateNodeStatus = function (moduleCode, nodeId, installStatus) { that.modules.forEach(function (mod) { if (mod.code == moduleCode && mod.clusters != null) { mod.clusters.forEach(function (cluster) { if (cluster.nodes != null) { cluster.nodes.forEach(function (node) { if (node.id == nodeId && installStatus != null) { node.installStatus = installStatus // 仅在升级完成(isFinished)时才移除跟踪 // 不要仅凭 !isRunning 就移除,因为批量升级时排队的节点尚未开始 if (installStatus.isFinished) { node.isUpgrading = false let idx = that.upgradingNodeIds[moduleCode].indexOf(nodeId) if (idx >= 0) { that.upgradingNodeIds[moduleCode].splice(idx, 1) } if (installStatus.isOk) { // 升级成功,延迟后从列表中移除 setTimeout(function () { let nIdx = cluster.nodes.indexOf(node) if (nIdx >= 0) { cluster.nodes.splice(nIdx, 1) cluster.count-- mod.count-- that.totalUpgradeCount-- if (cluster.count <= 0) { let cIdx = mod.clusters.indexOf(cluster) if (cIdx >= 0) { mod.clusters.splice(cIdx, 1) } } if (mod.count <= 0) { let mIdx = that.modules.indexOf(mod) if (mIdx >= 0) { that.modules.splice(mIdx, 1) } } } }, 2000) } else { // 升级失败,根据 errorCode 给出具体提示 that.handleUpgradeError(moduleCode, node, installStatus) } } } }) } }) } }) } /** * 处理升级错误,根据 errorCode 提供更友好的提示 */ this.handleUpgradeError = function (moduleCode, node, installStatus) { let errorCode = installStatus.errorCode || "" let errMsg = installStatus.error || "" switch (errorCode) { case "EMPTY_LOGIN": case "EMPTY_SSH_HOST": case "EMPTY_SSH_PORT": case "EMPTY_GRANT": // SSH 信息未配置的错误,不弹窗(页面上已有"没有设置"提示) break case "CREATE_ROOT_DIRECTORY_FAILED": teaweb.warn("节点 \"" + node.name + "\" 创建根目录失败:" + errMsg) break case "INSTALL_HELPER_FAILED": teaweb.warn("节点 \"" + node.name + "\" 安装助手失败:" + errMsg) break case "TEST_FAILED": teaweb.warn("节点 \"" + node.name + "\" 环境测试失败:" + errMsg) break case "RPC_TEST_FAILED": teaweb.warn("节点 \"" + node.name + "\" RPC通讯测试失败:" + errMsg) break } } /** * 更新自动升级状态 */ this.updateAutoUpgrade = function () { var vue = this // @change 已经翻转了值,先记录新值并立即恢复 let newValue = that.config.autoUpgrade that.config.autoUpgrade = !newValue let msg = newValue ? "确定要开启自动升级功能吗?开启后节点会自动下载安装新版本。" : "确定要关闭自动升级功能吗?关闭后只能通过这里手动执行升级。" teaweb.confirm(msg, function () { vue.$post("/settings/upgrade") .params({ autoUpgrade: newValue ? 1 : 0 }) .success(function () { that.config.autoUpgrade = newValue teaweb.successToast("设置保存成功") }) .fail(function (resp) { teaweb.warn("设置保存失败:" + resp.message) }) }) } })