fix: container name in add worker

This commit is contained in:
jialin
2025-12-17 14:59:07 +08:00
parent 6968f9fa6b
commit bd46fcdc75
22 changed files with 332 additions and 207 deletions
+1 -1
View File
@@ -164,4 +164,4 @@ const AlertInfo: React.FC<AlertInfoProps> = (props) => {
); );
}; };
export default React.memo(AlertInfo); export default AlertInfo;
+7 -1
View File
@@ -92,5 +92,11 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
'clusters.addworker.message.success': 'clusters.addworker.message.success':
'{count} workers have been added to the cluster.', '{count} workers have been added to the cluster.',
'clusters.create.serverUrl': 'Server URL', 'clusters.create.serverUrl': 'Server URL',
'clusters.create.workerConfig': 'Worker Configuration' 'clusters.create.workerConfig': 'Worker Configuration',
'clusters.addworker.containerName': 'Worker Container Name',
'clusters.addworker.containerName.tips':
'Specify a name for the worker container.',
'clusters.addworker.dataVolume': 'GPUStack Data Volume',
'clusters.addworker.dataVolume.tips':
'Specify a data storage path for GPUStack.'
}; };
+2 -1
View File
@@ -270,5 +270,6 @@ export default {
'Only designated users can access the model.', 'Only designated users can access the model.',
'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`, 'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
'models.catalog.nogpus.tips': 'models.catalog.nogpus.tips':
'No compatible GPUs are available in the selected cluster for this model.' 'No compatible GPUs are available in the selected cluster for this model.',
'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`
}; };
+2 -2
View File
@@ -19,12 +19,12 @@ export default {
'users.form.currentpassword': 'Current Password', 'users.form.currentpassword': 'Current Password',
'users.form.updatepassword': 'Modify Password', 'users.form.updatepassword': 'Modify Password',
'users.form.rule.password': 'users.form.rule.password':
'Contains uppercase and lowercase letters, numbers, and special characters(!@#$%^&*_+), 6 to 12 characters in length, no spaces allowed.', 'Contains uppercase and lowercase letters, numbers, and special characters(!@#$%^&*_+), 6 to 32 characters in length, no spaces allowed.',
'users.password.uppcase': 'At least one uppercase letter', 'users.password.uppcase': 'At least one uppercase letter',
'users.password.lowercase': 'At least one lowercase letter', 'users.password.lowercase': 'At least one lowercase letter',
'users.password.number': 'At least one number', 'users.password.number': 'At least one number',
'users.password.special': 'At least one special character', 'users.password.special': 'At least one special character',
'users.password.length': 'Length between 6 and 12 characters', 'users.password.length': 'Length between 6 and 32 characters',
'users.password.modify.title': 'Modify Password', 'users.password.modify.title': 'Modify Password',
'users.password.modify.description': 'users.password.modify.description':
"For your account's security, please change your initial password.", "For your account's security, please change your initial password.",
+11 -1
View File
@@ -92,7 +92,13 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
'clusters.addworker.message.success': 'clusters.addworker.message.success':
'{count} workers have been added to the cluster.', '{count} workers have been added to the cluster.',
'clusters.create.serverUrl': 'Server URL', 'clusters.create.serverUrl': 'Server URL',
'clusters.create.workerConfig': 'Worker Configuration' 'clusters.create.workerConfig': 'Worker Configuration',
'clusters.addworker.containerName': 'Worker Container Name',
'clusters.addworker.containerName.tips':
'Specify a name for the worker container.',
'clusters.addworker.dataVolume': 'GPUStack Data Volume',
'clusters.addworker.dataVolume.tips':
'Specify a data storage path for GPUStack.'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -172,4 +178,8 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
// 74. 'clusters.addworker.message.success': '{count} workers have been added to the cluster.', // 74. 'clusters.addworker.message.success': '{count} workers have been added to the cluster.',
// 75. 'clusters.create.serverUrl': 'Server URL', // 75. 'clusters.create.serverUrl': 'Server URL',
// 76. 'clusters.create.workerConfig': 'Worker Configuration' // 76. 'clusters.create.workerConfig': 'Worker Configuration'
// 75. 'clusters.addworker.containerName': 'Worker Container Name',
// 76. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
// 77. 'clusters.addworker.dataVolume': 'GPUStack Data Volume',
// 78. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.'
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+4 -2
View File
@@ -270,7 +270,8 @@ export default {
'Only designated users can access the model.', 'Only designated users can access the model.',
'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`, 'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
'models.catalog.nogpus.tips': 'models.catalog.nogpus.tips':
'No compatible GPUs are available in the selected cluster for this model.' 'No compatible GPUs are available in the selected cluster for this model.',
'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -363,5 +364,6 @@ export default {
// 70. 'models.accessSettings.authed.tips': 'Accessible to all authenticated platform users.', // 70. 'models.accessSettings.authed.tips': 'Accessible to all authenticated platform users.',
// 71.'models.accessSettings.allowedUsers.tips': 'Only designated users can access the model.', // 71.'models.accessSettings.allowedUsers.tips': 'Only designated users can access the model.',
// 72. 'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`, // 72. 'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
// 73. 'models.catalog.nogpus.tips': 'No compatible GPUs are available in the selected cluster for this model.' // 73. 'models.catalog.nogpus.tips': 'No compatible GPUs are available in the selected cluster for this model.',
// 74. 'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+2 -2
View File
@@ -20,12 +20,12 @@ export default {
'users.form.currentpassword': '現在のパスワード', 'users.form.currentpassword': '現在のパスワード',
'users.form.updatepassword': 'パスワードを変更', 'users.form.updatepassword': 'パスワードを変更',
'users.form.rule.password': 'users.form.rule.password':
'大文字と小文字、数字、特殊文字(!@#$%^&*_+)を含む6〜12文字で、スペースは使用できません。', '大文字と小文字、数字、特殊文字(!@#$%^&*_+)を含む6〜32文字で、スペースは使用できません。',
'users.password.uppcase': '少なくとも1つの大文字を含む', 'users.password.uppcase': '少なくとも1つの大文字を含む',
'users.password.lowercase': '少なくとも1つの小文字を含む', 'users.password.lowercase': '少なくとも1つの小文字を含む',
'users.password.number': '少なくとも1つの数字を含む', 'users.password.number': '少なくとも1つの数字を含む',
'users.password.special': '少なくとも1つの特殊文字を含む', 'users.password.special': '少なくとも1つの特殊文字を含む',
'users.password.length': '6〜12文字の長さ', 'users.password.length': '6〜32文字の長さ',
'users.password.modify.title': 'パスワードを変更', 'users.password.modify.title': 'パスワードを変更',
'users.password.modify.description': 'users.password.modify.description':
'アカウントのセキュリティのため、初期パスワードを変更してください。', 'アカウントのセキュリティのため、初期パスワードを変更してください。',
+11 -1
View File
@@ -92,7 +92,13 @@ export default {
'clusters.addworker.message.success': 'clusters.addworker.message.success':
'{count} workers have been added to the cluster.', '{count} workers have been added to the cluster.',
'clusters.create.serverUrl': 'Server URL', 'clusters.create.serverUrl': 'Server URL',
'clusters.create.workerConfig': 'Worker Configuration' 'clusters.create.workerConfig': 'Worker Configuration',
'clusters.addworker.containerName': 'Worker Container Name',
'clusters.addworker.containerName.tips':
'Specify a name for the worker container.',
'clusters.addworker.dataVolume': 'GPUStack Data Volume',
'clusters.addworker.dataVolume.tips':
'Specify a data storage path for GPUStack.'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -103,4 +109,8 @@ export default {
// 5. 'clusters.addworker.message.success': '{count} workers have been added to the cluster.', // 5. 'clusters.addworker.message.success': '{count} workers have been added to the cluster.',
// 6. 'clusters.create.serverUrl': 'Server URL', // 6. 'clusters.create.serverUrl': 'Server URL',
// 7. 'clusters.create.workerConfig': 'Worker Configuration' // 7. 'clusters.create.workerConfig': 'Worker Configuration'
// 6. 'clusters.addworker.containerName': 'Worker Container Name',
// 7. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
// 8. 'clusters.addworker.dataVolume': 'GPUStack Data Volume',
// 9. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.'
// ================================================================ // ================================================================
+4 -2
View File
@@ -275,7 +275,8 @@ export default {
'Only designated users can access the model.', 'Only designated users can access the model.',
'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`, 'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
'models.catalog.nogpus.tips': 'models.catalog.nogpus.tips':
'No compatible GPUs are available in the selected cluster for this model.' 'No compatible GPUs are available in the selected cluster for this model.',
'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -283,5 +284,6 @@ export default {
// 2. 'models.accessSettings.authed.tips': 'Accessible to all authenticated platform users.', // 2. 'models.accessSettings.authed.tips': 'Accessible to all authenticated platform users.',
// 3. 'models.accessSettings.allowedUsers.tips': 'Only designated users can access the model.', // 3. 'models.accessSettings.allowedUsers.tips': 'Only designated users can access the model.',
// 4. 'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`, // 4. 'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
// 5. 'models.catalog.nogpus.tips': 'No compatible GPUs are available in the selected cluster for this model.' // 5. 'models.catalog.nogpus.tips': 'No compatible GPUs are available in the selected cluster for this model.',
// 6. 'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+2 -2
View File
@@ -20,12 +20,12 @@ export default {
'users.form.currentpassword': 'Текущий пароль', 'users.form.currentpassword': 'Текущий пароль',
'users.form.updatepassword': 'Изменить пароль', 'users.form.updatepassword': 'Изменить пароль',
'users.form.rule.password': 'users.form.rule.password':
'Должен содержать заглавные и строчные буквы, цифры и спецсимволы (!@#$%^&*_+), длина 6-12 символов, без пробелов.', 'Должен содержать заглавные и строчные буквы, цифры и спецсимволы (!@#$%^&*_+), длина 6-32 символов, без пробелов.',
'users.password.uppcase': 'Минимум одна заглавная буква', 'users.password.uppcase': 'Минимум одна заглавная буква',
'users.password.lowercase': 'Минимум одна строчная буква', 'users.password.lowercase': 'Минимум одна строчная буква',
'users.password.number': 'Минимум одна цифра', 'users.password.number': 'Минимум одна цифра',
'users.password.special': 'Минимум один спецсимвол', 'users.password.special': 'Минимум один спецсимвол',
'users.password.length': 'Длина от 6 до 12 символов', 'users.password.length': 'Длина от 6 до 32 символов',
'users.password.modify.title': 'Смена пароля', 'users.password.modify.title': 'Смена пароля',
'users.password.modify.description': 'users.password.modify.description':
'В целях безопасности измените первоначальный пароль.', 'В целях безопасности измените первоначальный пароль.',
+5 -1
View File
@@ -90,5 +90,9 @@ export default {
'如果 <span class="bold-text">/opt/rocm</span> 目录不存在,请创建一个指向已安装 ROCm 路径的符号链接:<span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>。', '如果 <span class="bold-text">/opt/rocm</span> 目录不存在,请创建一个指向已安装 ROCm 路径的符号链接:<span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>。',
'clusters.addworker.message.success': '已将 {count} 个工作节点添加到集群中。', 'clusters.addworker.message.success': '已将 {count} 个工作节点添加到集群中。',
'clusters.create.serverUrl': '服务器地址', 'clusters.create.serverUrl': '服务器地址',
'clusters.create.workerConfig': '节点配置' 'clusters.create.workerConfig': '节点配置',
'clusters.addworker.containerName': '节点容器名称',
'clusters.addworker.containerName.tips': '为节点容器指定一个名称。',
'clusters.addworker.dataVolume': 'GPUStack 数据卷',
'clusters.addworker.dataVolume.tips': '为 GPUStack 指定数据存储路径。'
}; };
+3 -1
View File
@@ -253,5 +253,7 @@ export default {
'models.accessSettings.authed.tips': '平台内所有已认证用户可访问。', 'models.accessSettings.authed.tips': '平台内所有已认证用户可访问。',
'models.accessSettings.allowedUsers.tips': '仅允许选定的特定用户访问。', 'models.accessSettings.allowedUsers.tips': '仅允许选定的特定用户访问。',
'models.form.backendVersions.tips': `如需使用更多版本,请前往{link}页面并编辑对应的后端以添加版本。`, 'models.form.backendVersions.tips': `如需使用更多版本,请前往{link}页面并编辑对应的后端以添加版本。`,
'models.catalog.nogpus.tips': '所选集群中没有兼容该模型的 GPU。' 'models.catalog.nogpus.tips': '所选集群中没有兼容该模型的 GPU。',
'models.form.modelfile.notfound':
'你指定的模型文件路径在 GPUStack 服务器上不存在。建议在 GPUStack 服务器和 GPUStack 节点上使用相同的模型文件路径,这有助于 GPUStack 做出更优的调度与决策。'
}; };
+2 -2
View File
@@ -19,12 +19,12 @@ export default {
'users.form.currentpassword': '当前密码', 'users.form.currentpassword': '当前密码',
'users.form.updatepassword': '修改密码', 'users.form.updatepassword': '修改密码',
'users.form.rule.password': 'users.form.rule.password':
'包含大小写字母、数字和特殊字符(!@#$%^&*_+),6至12个字符,不允许有空格', '包含大小写字母、数字和特殊字符(!@#$%^&*_+),6至32个字符,不允许有空格',
'users.password.uppcase': '至少包含一个大写字母', 'users.password.uppcase': '至少包含一个大写字母',
'users.password.lowercase': '至少包含一个小写字母', 'users.password.lowercase': '至少包含一个小写字母',
'users.password.number': '至少包含一个数字', 'users.password.number': '至少包含一个数字',
'users.password.special': '至少包含一个特殊字符', 'users.password.special': '至少包含一个特殊字符',
'users.password.length': '长度在6至12个字符之间', 'users.password.length': '长度在6至32个字符之间',
'users.password.modify.title': '修改密码', 'users.password.modify.title': '修改密码',
'users.password.modify.description': '为了确保您的账户安全,请修改初始密码', 'users.password.modify.description': '为了确保您的账户安全,请修改初始密码',
'users.password.confirm': '确认新密码', 'users.password.confirm': '确认新密码',
@@ -7,6 +7,8 @@ type ViewModalProps = {
workerIP?: string; workerIP?: string;
modelDir?: string; modelDir?: string;
cacheDir?: string; cacheDir?: string;
containerName?: string;
gpustackDataVolume?: string;
registrationInfo: { registrationInfo: {
token: string; token: string;
image: string; image: string;
@@ -19,7 +21,9 @@ const AddWorkerCommand: React.FC<ViewModalProps> = ({
workerIP, workerIP,
modelDir, modelDir,
cacheDir, cacheDir,
currentGPU currentGPU,
containerName,
gpustackDataVolume
}) => { }) => {
const code = React.useMemo(() => { const code = React.useMemo(() => {
const commandCode = addWorkerGuide['all']; const commandCode = addWorkerGuide['all'];
@@ -31,13 +35,23 @@ const AddWorkerCommand: React.FC<ViewModalProps> = ({
workerIP: workerIP, workerIP: workerIP,
modelDir: modelDir, modelDir: modelDir,
cacheDir: cacheDir, cacheDir: cacheDir,
containerName: containerName,
gpustackDataVolume: gpustackDataVolume,
image: registrationInfo.image, image: registrationInfo.image,
token: registrationInfo.token || '${token}' token: registrationInfo.token || '${token}'
}) })
?.trim() ?.trim()
.replace(/\s+$/gm, '') .replace(/\s+$/gm, '')
.replace(/\\+$/, ''); .replace(/\\+$/, '');
}, [registrationInfo, currentGPU, workerIP, modelDir, cacheDir]); }, [
registrationInfo,
currentGPU,
workerIP,
modelDir,
cacheDir,
containerName,
gpustackDataVolume
]);
return ( return (
<HighlightCode <HighlightCode
@@ -51,6 +51,14 @@ export interface SummaryDataKeys {
ip: string; ip: string;
required: boolean; required: boolean;
}; };
containerNameConfig: {
enabled: boolean;
name: string;
};
gpustackDataVolumeConfig: {
enabled: boolean;
path: string;
};
} }
export type SummaryDataMap = { export type SummaryDataMap = {
@@ -25,6 +25,15 @@ const DockerRunCommand = () => {
enable: false, enable: false,
path: '' path: ''
}; };
const containerNameConfig = summary.get('containerNameConfig') || {
enable: false,
name: ''
};
const gpustackDataVolumeConfig = summary.get('gpustackDataVolumeConfig') || {
enable: false,
path: ''
};
const currentGPU = summary.get('currentGPU') || ''; const currentGPU = summary.get('currentGPU') || '';
const stepIndex = stepList.indexOf(StepNamesMap.RunCommand) + 1; const stepIndex = stepList.indexOf(StepNamesMap.RunCommand) + 1;
@@ -56,6 +65,12 @@ const DockerRunCommand = () => {
workerIP={workerIPConfig.enable ? workerIPConfig.ip : ''} workerIP={workerIPConfig.enable ? workerIPConfig.ip : ''}
modelDir={modelDirConfig.enable ? modelDirConfig.path : ''} modelDir={modelDirConfig.enable ? modelDirConfig.path : ''}
cacheDir={cacheDirConfig.enable ? cacheDirConfig.path : ''} cacheDir={cacheDirConfig.enable ? cacheDirConfig.path : ''}
containerName={
containerNameConfig.enable ? containerNameConfig.name : ''
}
gpustackDataVolume={
gpustackDataVolumeConfig.enable ? gpustackDataVolumeConfig.path : ''
}
currentGPU={currentGPU} currentGPU={currentGPU}
/> />
</StepCollapse> </StepCollapse>
@@ -96,6 +96,16 @@ const SpecifyArguments = () => {
path: '' path: ''
}; };
const containerNameConfig = summary.get('containerNameConfig') || {
enable: false,
name: ''
};
const gpustackDataVolumeConfig = summary.get('gpustackDataVolumeConfig') || {
enable: false,
path: ''
};
const setWorkerIPConfig = (config: { const setWorkerIPConfig = (config: {
enable: boolean; enable: boolean;
ip?: string; ip?: string;
@@ -138,10 +148,16 @@ const SpecifyArguments = () => {
const unregisterWorkerIP = registerField('workerIPConfig'); const unregisterWorkerIP = registerField('workerIPConfig');
const unregisterModelDir = registerField('modelDirConfig'); const unregisterModelDir = registerField('modelDirConfig');
const unregisterCacheDir = registerField('cacheDirConfig'); const unregisterCacheDir = registerField('cacheDirConfig');
const unregisterContainerName = registerField('containerNameConfig');
const unregisterGpustackDataVolume = registerField(
'gpustackDataVolumeConfig'
);
return () => { return () => {
unregisterWorkerIP(); unregisterWorkerIP();
unregisterModelDir(); unregisterModelDir();
unregisterCacheDir(); unregisterCacheDir();
unregisterContainerName();
unregisterGpustackDataVolume();
}; };
}, []); }, []);
@@ -161,6 +177,16 @@ const SpecifyArguments = () => {
enable: false, enable: false,
path: '' path: ''
}); });
updateField('containerNameConfig', {
enable: false,
name: ''
});
updateField('gpustackDataVolumeConfig', {
enable: false,
path: ''
});
}, []); }, []);
return ( return (
@@ -246,6 +272,60 @@ const SpecifyArguments = () => {
) )
} }
></SwitchSetting> ></SwitchSetting>
{/* container name config */}
<SwitchSetting
label={intl.formatMessage({ id: 'clusters.addworker.containerName' })}
tips={intl.formatMessage({
id: 'clusters.addworker.containerName.tips'
})}
placeholder={intl.formatMessage(
{
id: 'common.help.default'
},
{ content: 'gpustack-worker' }
)}
checked={containerNameConfig.enable}
value={containerNameConfig.name}
onChange={(checked) =>
updateField('containerNameConfig', {
...containerNameConfig,
enable: checked
})
}
onInputChange={(value) =>
updateField('containerNameConfig', {
...containerNameConfig,
name: value
})
}
></SwitchSetting>
{/* gpustack data volume config */}
<SwitchSetting
label={intl.formatMessage({ id: 'clusters.addworker.dataVolume' })}
tips={intl.formatMessage({
id: 'clusters.addworker.dataVolume.tips'
})}
placeholder={intl.formatMessage(
{
id: 'common.help.default'
},
{ content: 'gpustack-data' }
)}
checked={gpustackDataVolumeConfig.enable}
value={gpustackDataVolumeConfig.path}
onChange={(checked) =>
updateField('gpustackDataVolumeConfig', {
...gpustackDataVolumeConfig,
enable: checked
})
}
onInputChange={(value) =>
updateField('gpustackDataVolumeConfig', {
...gpustackDataVolumeConfig,
path: value
})
}
></SwitchSetting>
{/* model directory config */} {/* model directory config */}
<SwitchSetting <SwitchSetting
label={intl.formatMessage({ id: 'clusters.addworker.extraVolume' })} label={intl.formatMessage({ id: 'clusters.addworker.extraVolume' })}
@@ -5,10 +5,77 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import React from 'react'; import React from 'react';
import styled from 'styled-components';
import { useAddWorkerContext } from './add-worker-context'; import { useAddWorkerContext } from './add-worker-context';
import { StepNamesMap } from './config'; import { StepNamesMap } from './config';
import { ConfigWrapper, Title } from './constainers'; import { ConfigWrapper, Title } from './constainers';
interface DataItemProps {
enable: boolean;
value: string;
required?: boolean;
label: string;
tips?: string;
}
const DataItemWrapper = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
.label {
color: var(--ant-color-text);
}
.value {
display: flex;
align-items: center;
}
`;
const DataItem: React.FC<DataItemProps> = ({
enable,
value,
required,
label,
tips
}) => {
return (
<DataItemWrapper>
<span className="label">{label}:</span>
<span className="value">
<span>{enable && value ? value : ''}</span>
{tips}
{(!value || !enable) && !required && (
<StopOutlined
style={{
color: 'var(--ant-color-text-tertiary)'
}}
/>
)}
{((enable && value) || (required && !enable)) && (
<CheckCircleOutlined
style={{
color: 'var(--ant-color-success)',
marginLeft: 4
}}
/>
)}
{enable && !value && required && (
<>
<WarningOutlined
style={{
color: 'var(--ant-color-warning)',
marginLeft: 4
}}
/>
</>
)}
</span>
</DataItemWrapper>
);
};
const SummaryData: React.FC = () => { const SummaryData: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const { summary, stepList } = useAddWorkerContext(); const { summary, stepList } = useAddWorkerContext();
@@ -32,6 +99,16 @@ const SummaryData: React.FC = () => {
path: '' path: ''
}; };
const containerNameConfig = summary.get('containerNameConfig') || {
enable: false,
name: ''
};
const gpustackDataVolumeConfig = summary.get('gpustackDataVolumeConfig') || {
enable: false,
path: ''
};
return ( return (
<ConfigWrapper> <ConfigWrapper>
<Title> <Title>
@@ -39,127 +116,56 @@ const SummaryData: React.FC = () => {
</Title> </Title>
<div className="config-content"> <div className="config-content">
{stepList.includes(StepNamesMap.SelectCluster) && ( {stepList.includes(StepNamesMap.SelectCluster) && (
<div className="item"> <DataItem
<span className="label"> value={clusterName}
{intl.formatMessage({ id: 'clusters.title' })}: enable={true}
</span> label={intl.formatMessage({ id: 'clusters.title' })}
<span className="value"> ></DataItem>
{clusterName}
{clusterName ? (
<CheckCircleOutlined
style={{
color: 'var(--ant-color-success)',
marginLeft: 4
}}
/>
) : (
<WarningOutlined
style={{
color: 'var(--ant-color-warning)',
marginLeft: 4
}}
/>
)}
</span>
</div>
)} )}
<div className="item">
<span className="label"> <DataItem
{intl.formatMessage({ id: 'clusters.addworker.gpuVendor' })}: label={intl.formatMessage({ id: 'clusters.addworker.gpuVendor' })}
</span> enable={true}
<span className="value"> value={workerCommand.label}
{/* for checked style */} ></DataItem>
{workerCommand.label}
<CheckCircleOutlined <DataItem
style={{ label={intl.formatMessage({ id: 'clusters.addworker.workerIP' })}
color: 'var(--ant-color-success)', tips={
marginLeft: 4 workerIPConfig.enable
}}
/>
</span>
</div>
<div className="item">
<span className="label">
{intl.formatMessage({ id: 'clusters.addworker.workerIP' })}:
</span>
<span className="value">
{/* for invalidate style */}
{workerIPConfig.enable
? workerIPConfig.ip ? workerIPConfig.ip
? workerIPConfig.ip ? workerIPConfig.ip
: intl.formatMessage({ id: 'clusters.addworker.notSpecified' }) : intl.formatMessage({ id: 'clusters.addworker.notSpecified' })
: intl.formatMessage({ id: 'clusters.addworker.autoDetect' })} : intl.formatMessage({ id: 'clusters.addworker.autoDetect' })
}
enable={workerIPConfig.enable}
value={workerIPConfig.ip}
required={true}
></DataItem>
{workerIPConfig.enable && !workerIPConfig.ip && ( <DataItem
<WarningOutlined label={intl.formatMessage({ id: 'clusters.addworker.containerName' })}
style={{ enable={containerNameConfig.enable}
color: 'var(--ant-color-warning)', value={containerNameConfig.name}
marginLeft: 4 ></DataItem>
}}
/>
)}
{(!workerIPConfig.enable || workerIPConfig.ip) && (
<CheckCircleOutlined
style={{
color: 'var(--ant-color-success)',
marginLeft: 4
}}
/>
)}
</span>
</div>
<div className="item">
<span className="label">
{intl.formatMessage({ id: 'clusters.addworker.extraVolume' })}:
</span>
<span className="value">
{modelDirConfig.enable && modelDirConfig.path
? modelDirConfig.path
: ''}
{(!modelDirConfig.path || !modelDirConfig.enable) && (
<StopOutlined
style={{
color: 'var(--ant-color-text-tertiary)'
}}
/>
)}
{modelDirConfig.enable && modelDirConfig.path && ( <DataItem
<CheckCircleOutlined label={intl.formatMessage({ id: 'clusters.addworker.dataVolume' })}
style={{ enable={gpustackDataVolumeConfig.enable}
color: 'var(--ant-color-success)', value={gpustackDataVolumeConfig.path}
marginLeft: 4 ></DataItem>
}}
/>
)}
</span>
</div>
<div className="item">
<span className="label">
{intl.formatMessage({ id: 'clusters.addworker.cacheVolume' })}:
</span>
<span className="value">
{cacheDirConfig.enable && cacheDirConfig.path
? cacheDirConfig.path
: ''}
{(!cacheDirConfig.path || !cacheDirConfig.enable) && (
<StopOutlined
style={{
color: 'var(--ant-color-text-tertiary)'
}}
/>
)}
{cacheDirConfig.enable && cacheDirConfig.path && ( <DataItem
<CheckCircleOutlined label={intl.formatMessage({ id: 'clusters.addworker.extraVolume' })}
style={{ enable={modelDirConfig.enable}
color: 'var(--ant-color-success)', value={modelDirConfig.path}
marginLeft: 4 ></DataItem>
}}
/> <DataItem
)} label={intl.formatMessage({ id: 'clusters.addworker.cacheVolume' })}
</span> enable={cacheDirConfig.enable}
</div> value={cacheDirConfig.path}
></DataItem>
</div> </div>
</ConfigWrapper> </ConfigWrapper>
); );
+1
View File
@@ -184,6 +184,7 @@ export interface CatalogItem {
name: string; name: string;
id: number; id: number;
description: string; description: string;
deployment_notes?: string;
home: string; home: string;
icon: string; icon: string;
categories: string[]; categories: string[];
+10 -5
View File
@@ -271,15 +271,20 @@ export const useCheckCompatibility = () => {
// current cluster is not available, but other clusters are available // current cluster is not available, but other clusters are available
const othersAvailable = !hasClaim && resourceClaimMap.size > 0; const othersAvailable = !hasClaim && resourceClaimMap.size > 0;
let compatibilityMessage = compatibility_messages.join(' ');
compatibilityMessage = compatibilityMessage.startsWith(
'The model file path you specified does not exist on the GPUStack server'
)
? intl.formatMessage({ id: 'models.form.modelfile.notfound' })
: compatibilityMessage;
let msgData = { let msgData = {
title: title: scheduling_messages?.length > 0 ? compatibilityMessage : '',
scheduling_messages?.length > 0
? compatibility_messages?.join(' ')
: '',
message: message:
scheduling_messages?.length > 0 scheduling_messages?.length > 0
? scheduling_messages ? scheduling_messages
: compatibility_messages?.join(' ') : compatibilityMessage
}; };
if (hasClaim) { if (hasClaim) {
+1
View File
@@ -29,6 +29,7 @@ export const logout = async (userInfo?: any) => {
}); });
clearStorageUserSettings(); clearStorageUserSettings();
clearAtomStorage(userAtom); clearAtomStorage(userAtom);
if (res?.logout_url) { if (res?.logout_url) {
window.location.href = res.logout_url; window.location.href = res.logout_url;
} }
+25 -67
View File
@@ -129,15 +129,28 @@ export const dockerEnvCommandMap = {
GPUsConfigs[GPUDriverMap.METAX] GPUsConfigs[GPUDriverMap.METAX]
) )
}; };
interface AddWorkerCommandParams {
server: string;
tag: string;
token: string;
image: string;
gpu: string;
workerIP?: string;
modelDir?: string;
containerName?: string;
gpustackDataVolume?: string;
cacheDir?: string;
}
const setNormalArgs = (params: any) => { const setNormalArgs = (params: any) => {
return `CONTAINER_NAME="gpustack-worker" return `sudo docker run -d --name ${params.containerName || 'gpustack-worker'} \\
sudo docker run -d --name "\${CONTAINER_NAME}" \\ -e "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_NAME=${params.containerName || 'gpustack-worker'}" \\
-e "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_NAME=\${CONTAINER_NAME}" \\
--restart=unless-stopped \\ --restart=unless-stopped \\
--privileged \\ --privileged \\
--network=host \\ --network=host \\
--volume /var/run/docker.sock:/var/run/docker.sock \\ --volume /var/run/docker.sock:/var/run/docker.sock \\
--volume gpustack-data:/var/lib/gpustack \\ --volume ${params.gpustackDataVolume || 'gpustack-data'}:/var/lib/gpustack \\
${params.modelDir ? `--volume ${params.modelDir}:${params.modelDir} \\` : ''} ${params.modelDir ? `--volume ${params.modelDir}:${params.modelDir} \\` : ''}
${params.cacheDir ? `--volume ${params.cacheDir}:/var/lib/gpustack/cache \\` : ''}`; ${params.cacheDir ? `--volume ${params.cacheDir}:/var/lib/gpustack/cache \\` : ''}`;
}; };
@@ -149,18 +162,11 @@ const setImageArgs = (params: any) => {
}; };
// avaliable for NVIDIA、MThreads // avaliable for NVIDIA、MThreads
const registerWorker = (params: { const registerWorker = (params: AddWorkerCommandParams) => {
server: string;
tag: string;
token: string;
image: string;
gpu: string;
workerIP?: string;
modelDir?: string;
}) => {
const config = GPUsConfigs[params.gpu]; const config = GPUsConfigs[params.gpu];
const commonArgs = setNormalArgs(params); const commonArgs = setNormalArgs(params);
const imageArgs = setImageArgs(params); const imageArgs = setImageArgs(params);
// remove empty enter lines and trailing backslash // remove empty enter lines and trailing backslash
return `${commonArgs} return `${commonArgs}
--runtime ${config.runtime} \\ --runtime ${config.runtime} \\
@@ -169,15 +175,7 @@ const registerWorker = (params: {
}; };
// avaliable for AMD // avaliable for AMD
const registerAMDWorker = (params: { const registerAMDWorker = (params: AddWorkerCommandParams) => {
server: string;
tag: string;
token: string;
image: string;
gpu: string;
workerIP?: string;
modelDir?: string;
}) => {
const config = GPUsConfigs[params.gpu]; const config = GPUsConfigs[params.gpu];
const commonArgs = setNormalArgs(params); const commonArgs = setNormalArgs(params);
const imageArgs = setImageArgs(params); const imageArgs = setImageArgs(params);
@@ -190,15 +188,7 @@ const registerAMDWorker = (params: {
}; };
// avaliable for Ascend // avaliable for Ascend
const registerAscendWorker = (params: { const registerAscendWorker = (params: AddWorkerCommandParams) => {
server: string;
tag: string;
token: string;
image: string;
gpu: string;
workerIP?: string;
modelDir?: string;
}) => {
const config = GPUsConfigs[params.gpu]; const config = GPUsConfigs[params.gpu];
const commonArgs = setNormalArgs(params); const commonArgs = setNormalArgs(params);
const imageArgs = setImageArgs(params); const imageArgs = setImageArgs(params);
@@ -209,15 +199,7 @@ const registerAscendWorker = (params: {
${params.workerIP ? `--advertise-address ${params.workerIP} \\` : ''}`; ${params.workerIP ? `--advertise-address ${params.workerIP} \\` : ''}`;
}; };
const registerHygonWorker = (params: { const registerHygonWorker = (params: AddWorkerCommandParams) => {
server: string;
tag: string;
token: string;
image: string;
gpu: string;
workerIP?: string;
modelDir?: string;
}) => {
const config = GPUsConfigs[params.gpu]; const config = GPUsConfigs[params.gpu];
const commonArgs = setNormalArgs(params); const commonArgs = setNormalArgs(params);
const imageArgs = setImageArgs(params); const imageArgs = setImageArgs(params);
@@ -230,15 +212,7 @@ const registerHygonWorker = (params: {
${params.workerIP ? `--advertise-address ${params.workerIP} \\` : ''}`; ${params.workerIP ? `--advertise-address ${params.workerIP} \\` : ''}`;
}; };
const registerIluvatarWorker = (params: { const registerIluvatarWorker = (params: AddWorkerCommandParams) => {
server: string;
tag: string;
token: string;
image: string;
gpu: string;
workerIP?: string;
modelDir?: string;
}) => {
const config = GPUsConfigs[params.gpu]; const config = GPUsConfigs[params.gpu];
const commonArgs = setNormalArgs(params); const commonArgs = setNormalArgs(params);
const imageArgs = setImageArgs(params); const imageArgs = setImageArgs(params);
@@ -251,15 +225,7 @@ const registerIluvatarWorker = (params: {
${params.workerIP ? `--advertise-address ${params.workerIP} \\` : ''}`; ${params.workerIP ? `--advertise-address ${params.workerIP} \\` : ''}`;
}; };
const registerMetaXWorker = (params: { const registerMetaXWorker = (params: AddWorkerCommandParams) => {
server: string;
tag: string;
token: string;
image: string;
gpu: string;
workerIP?: string;
modelDir?: string;
}) => {
const config = GPUsConfigs[params.gpu]; const config = GPUsConfigs[params.gpu];
const commonArgs = setNormalArgs(params); const commonArgs = setNormalArgs(params);
const imageArgs = setImageArgs(params); const imageArgs = setImageArgs(params);
@@ -270,15 +236,7 @@ const registerMetaXWorker = (params: {
${params.workerIP ? `--advertise-address ${params.workerIP} \\` : ''}`; ${params.workerIP ? `--advertise-address ${params.workerIP} \\` : ''}`;
}; };
const registerCambriconWorker = (params: { const registerCambriconWorker = (params: AddWorkerCommandParams) => {
server: string;
tag: string;
token: string;
image: string;
gpu: string;
workerIP?: string;
modelDir?: string;
}) => {
const config = GPUsConfigs[params.gpu]; const config = GPUsConfigs[params.gpu];
const commonArgs = setNormalArgs(params); const commonArgs = setNormalArgs(params);
const imageArgs = setImageArgs(params); const imageArgs = setImageArgs(params);