diff --git a/src/frontend/src/domain/model/task/task-file-step.js b/src/frontend/src/domain/model/task/task-file-step.js index 6a1377a1dc..bdfbab020d 100644 --- a/src/frontend/src/domain/model/task/task-file-step.js +++ b/src/frontend/src/domain/model/task/task-file-step.js @@ -25,7 +25,6 @@ import _ from 'lodash'; import I18n from '@/i18n'; -// import HostNode from '@model/host-node' import TaskHostNodeModel from '@model/task-host-node'; const transferModeMap = { @@ -35,6 +34,9 @@ const transferModeMap = { }; export default class TaskFileStep { + static TYPE_SERVER = 1 + static TYPE_LOCAL = 2 + static TYPE_SOURCE = 3 constructor (payload = {}) { this.timeout = payload.timeout; this.uploadSpeedLimit = payload.uploadSpeedLimit || 0; diff --git a/src/frontend/src/utils/assist/business.js b/src/frontend/src/utils/assist/business.js index 1e71e84de0..b0d61b67b1 100644 --- a/src/frontend/src/utils/assist/business.js +++ b/src/frontend/src/utils/assist/business.js @@ -108,3 +108,110 @@ export const checkPublicScript = (route) => { } return false; }; + +export const compareHost = (preHost, nextHost) => { + // 全都使用了主机变量 + if (nextHost.variable && nextHost.variable === preHost.variable) { + return true; + } + // 服务文件主机手动添加 + // 目标服务器主机使用主机变量 + if (nextHost.variable) { + return false; + } + // 全都手动添加对比值 + const { + ipList: preIPList, + topoNodeList: preNodeList, + dynamicGroupList: preGroupList, + } = preHost.hostNodeInfo; + const { + ipList: nextIPList, + topoNodeList: nextNodeList, + dynamicGroupList: nextGroupList, + } = nextHost.hostNodeInfo; + // 对比主机 + if (preIPList.length !== nextIPList.length) { + return false; + } + const genHostKey = host => `${host.cloudAreaInfo.id}:${host.ip}`; + const preIPMap = preIPList.reduce((result, host) => { + result[genHostKey(host)] = true; + return result; + }, {}); + // eslint-disable-next-line no-plusplus + for (let i = 0; i < nextIPList.length; i++) { + if (!preIPMap[genHostKey(nextIPList[i])]) { + return false; + } + } + // 对比节点 + if (preNodeList.length !== nextNodeList.length) { + return false; + } + const genNodeKey = node => `#${node.id}#${node.type}`; + const taretNodeMap = preNodeList.reduce((result, node) => { + result[genNodeKey(node)] = true; + return result; + }, {}); + // eslint-disable-next-line no-plusplus + for (let i = 0; i < nextNodeList.length; i++) { + if (!taretNodeMap[genNodeKey(nextNodeList[i])]) { + return false; + } + } + // 对比分组 + if (preGroupList.length !== nextGroupList.length) { + return false; + } + const preGroupMap = preGroupList.reduce((result, groupId) => { + result[groupId] = true; + return result; + }, {}); + // eslint-disable-next-line no-plusplus + for (let i = 0; i < nextGroupList.length; i++) { + if (!preGroupMap[nextGroupList[i]]) { + return false; + } + } + return true; +}; + +export const detectionSourceFileDupLocation = (fileSourceList) => { + const fileLocationMap = {}; + const pathReg = /([^/]+\/?)\*?$/; + // 路径中以 * 结尾表示分发所有文件,可能和分发具体文件冲突 + let hasDirAllFile = false; + let hasFile = false; + // eslint-disable-next-line no-plusplus + for (let i = 0; i < fileSourceList.length; i++) { + const currentFileSource = fileSourceList[i]; + // eslint-disable-next-line no-plusplus + for (let j = 0; j < currentFileSource.fileLocation.length; j++) { + const currentFileLocation = currentFileSource.fileLocation[j]; + // 分发所有文件 + if (/\*$/.test(currentFileLocation)) { + hasDirAllFile = true; + if (hasFile) { + return true; + } + continue; + } + // 分发具体的文件 + if (!/(\/|(\/\*))$/.test(currentFileLocation)) { + hasFile = true; + if (hasDirAllFile) { + return true; + } + } + const pathMatch = currentFileLocation.match(pathReg); + if (pathMatch) { + if (fileLocationMap[pathMatch[1]]) { + return true; + } + fileLocationMap[pathMatch[1]] = 1; + } + } + } + return false; +}; diff --git a/src/frontend/src/views/fast-execution/distro-file/index.vue b/src/frontend/src/views/fast-execution/distro-file/index.vue index 74dbbced60..a588a62a24 100644 --- a/src/frontend/src/views/fast-execution/distro-file/index.vue +++ b/src/frontend/src/views/fast-execution/distro-file/index.vue @@ -122,12 +122,15 @@ } from 'vuex'; import I18n from '@/i18n'; import TaskExecuteService from '@service/task-execute'; + import TaskStepModel from '@model/task/task-step'; import TaskHostNodeModel from '@model/task-host-node'; import JbForm from '@components/jb-form'; import CardLayout from '@components/task-step/file/card-layout'; import ItemFactory from '@components/task-step/file/item-factory'; import { getScriptName, + compareHost, + detectionSourceFileDupLocation, } from '@utils/assist'; import { pushFileHistory, @@ -347,71 +350,62 @@ }, }); })) - // 检测源文件的同名文件和目录 + // 检测服务器源文件的主机和执行目标服务器主机相同 .then(() => new Promise((resolve, reject) => { - const fileLocationMap = {}; - const pathReg = /([^/]+\/?)\*?$/; - let isDouble = false; - // 路径中以 * 结尾表示分发所有文件,可能和分发具体文件冲突 - let hasDirAllFile = false; - let hasFile = false; + let sameHost = false; // eslint-disable-next-line no-plusplus for (let i = 0; i < this.formData.fileSourceList.length; i++) { const currentFileSource = this.formData.fileSourceList[i]; - // eslint-disable-next-line no-plusplus - for (let j = 0; j < currentFileSource.fileLocation.length; j++) { - const currentFileLocation = currentFileSource.fileLocation[j]; - // 分发所有文件 - if (/\*$/.test(currentFileLocation)) { - hasDirAllFile = true; - if (hasFile) { - isDouble = true; - break; - } - continue; - } - // 分发具体的文件 - if (!/(\/|(\/\*))$/.test(currentFileLocation)) { - hasFile = true; - if (hasDirAllFile) { - isDouble = true; - break; - } - } - const pathMatch = currentFileLocation.match(pathReg); - if (pathMatch) { - if (fileLocationMap[pathMatch[1]]) { - isDouble = true; - break; - } else { - fileLocationMap[pathMatch[1]] = 1; - } + // 服务器源文件 + if (currentFileSource.fileType === TaskStepModel.fileStep.TYPE_SERVER) { + if (compareHost(this.formData.server, currentFileSource.host)) { + sameHost = true; + break; } } } - - if (!isDouble) { + if (sameHost) { + this.$bkInfo({ + title: I18n.t('execution.源和目标服务器相同'), + subTitle: I18n.t('execution.检测到文件传输源和目标服务器是同一批,若是单台建议使用本地 cp 方式效率会更高,请问你是否确定参数无误?'), + width: 500, + okText: I18n.t('execution.好的,我调整一下'), + cancelText: I18n.t('execution.是的,确定无误'), + confirmFn: () => { + reject(new Error('execute')); + }, + cancelFn: () => { + resolve(); + }, + }); + } else { + resolve(); + } + })) + // 检测源文件的同名文件和目录 + .then(() => new Promise((resolve, reject) => { + if (detectionSourceFileDupLocation(this.formData.fileSourceList)) { + // 有重名目录和文件 + this.$bkInfo({ + title: I18n.t('execution.源文件可能出现同名'), + subTitle: I18n.t('execution.多文件源传输场景下容易出现同名文件覆盖的问题,你可以在目标路径中使用 [源服务器IP] 的变量来尽可能规避风险。'), + okText: I18n.t('execution.好的,我调整一下'), + cancelText: I18n.t('execution.已知悉,确定执行'), + closeIcon: false, + width: 500, + confirmFn: () => { + // 聚焦到目标路径输入框 + this.$refs.targetPath.$el.scrollIntoView(); + this.$refs.targetPath.$el.querySelector('.bk-form-input').focus(); + reject(new Error('transferMode change')); + }, + cancelFn: () => { + resolve(); + }, + }); + } else { resolve(); - return; } - // 有重名目录和文件 - this.$bkInfo({ - title: I18n.t('execution.源文件可能出现同名'), - subTitle: I18n.t('execution.多文件源传输场景下容易出现同名文件覆盖的问题,你可以在目标路径中使用 [源服务器IP] 的变量来尽可能规避风险。'), - okText: I18n.t('execution.好的,我调整一下'), - cancelText: I18n.t('execution.已知悉,确定执行'), - closeIcon: false, - width: 500, - confirmFn: () => { - // 聚焦到目标路径输入框 - this.$refs.targetPath.$el.scrollIntoView(); - this.$refs.targetPath.$el.querySelector('.bk-form-input').focus(); - reject(new Error('transferMode change')); - }, - cancelFn: () => { - resolve(); - }, - }); })) .then(() => { const { diff --git a/src/frontend/src/views/fast-execution/local.js b/src/frontend/src/views/fast-execution/local.js index 1f52b0483a..5fa604c9ff 100644 --- a/src/frontend/src/views/fast-execution/local.js +++ b/src/frontend/src/views/fast-execution/local.js @@ -48,5 +48,8 @@ export default { '多文件源传输场景下容易出现同名文件覆盖的问题,你可以在目标路径中使用 [源服务器IP] 的变量来尽可能规避风险。': 'Files may have the risk of overwriting with the same name in multi-file source, using built-in variable like [FILESRCIP] in the Dst. path to avoid risks as much as possible.', '好的,我调整一下': 'Oops... Adjust Now', '已知悉,确定执行': 'I\'m Sure, Keep Going', + 源和目标服务器相同: '', + '检测到文件传输源和目标服务器是同一批,若是单台建议使用本地 cp 方式效率会更高,请问你是否确定参数无误?': '', + '是的,确定无误': '', }, }; diff --git a/src/frontend/src/views/task-manage/common/render-task-step/task-step/distro-file.vue b/src/frontend/src/views/task-manage/common/render-task-step/task-step/distro-file.vue index 781e156543..8e1dbc4eaa 100644 --- a/src/frontend/src/views/task-manage/common/render-task-step/task-step/distro-file.vue +++ b/src/frontend/src/views/task-manage/common/render-task-step/task-step/distro-file.vue @@ -97,14 +97,15 @@