feat: worker maintenance
This commit is contained in:
@@ -235,6 +235,7 @@ body {
|
|||||||
|
|
||||||
.ant-pro-sider-logo-collapsed {
|
.ant-pro-sider-logo-collapsed {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
|
cursor: e-resize;
|
||||||
|
|
||||||
.collapse-wrap {
|
.collapse-wrap {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
+12
-13
@@ -285,22 +285,21 @@ export default (props: any) => {
|
|||||||
{logo}
|
{logo}
|
||||||
<div className="collapse-wrap" onClick={handleToggleCollapse}>
|
<div className="collapse-wrap" onClick={handleToggleCollapse}>
|
||||||
<Button
|
<Button
|
||||||
style={{ marginRight: collapsed ? 0 : -14, border: 'none' }}
|
style={{
|
||||||
|
marginRight: collapsed ? 0 : -14,
|
||||||
|
border: 'none',
|
||||||
|
cursor: 'w-resize'
|
||||||
|
}}
|
||||||
size="small"
|
size="small"
|
||||||
type={collapsed ? 'default' : 'text'}
|
type={collapsed ? 'default' : 'text'}
|
||||||
>
|
>
|
||||||
<>
|
<IconFont
|
||||||
<IconFont
|
type={collapsed ? 'icon-expand-left' : 'icon-expand-right'}
|
||||||
type="icon-expand-left"
|
className="font-size-18 text-secondary"
|
||||||
className="font-size-18 text-secondary"
|
style={{
|
||||||
style={{ display: collapsed ? 'block' : 'none' }}
|
display: 'block'
|
||||||
/>
|
}}
|
||||||
<IconFont
|
/>
|
||||||
type="icon-expand-right"
|
|
||||||
className="font-size-18 text-secondary"
|
|
||||||
style={{ display: !collapsed ? 'block' : 'none' }}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -96,5 +96,13 @@ export default {
|
|||||||
'resources.model.instance': 'Model Instance',
|
'resources.model.instance': 'Model Instance',
|
||||||
'resources.worker.download.privatekey': 'Download Private Key',
|
'resources.worker.download.privatekey': 'Download Private Key',
|
||||||
'resources.modelfiles.form.exsting': 'Downloaded',
|
'resources.modelfiles.form.exsting': 'Downloaded',
|
||||||
'resources.modelfiles.form.added': 'Added'
|
'resources.modelfiles.form.added': 'Added',
|
||||||
|
'resources.worker.maintenance.title': 'System Maintenance',
|
||||||
|
'resources.worker.maintenance.enable': 'Enter Maintenance Mode',
|
||||||
|
'resources.worker.maintenance.disable': 'Exit Maintenance Mode',
|
||||||
|
'resources.worker.maintenance.remark': 'Remark',
|
||||||
|
'resources.worker.maintenance.remark.rules':
|
||||||
|
'Please enter maintenance remarks',
|
||||||
|
'resources.worker.maintenance.tips':
|
||||||
|
'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -97,7 +97,15 @@ export default {
|
|||||||
'resources.worker.download.privatekey': 'Download Private Key',
|
'resources.worker.download.privatekey': 'Download Private Key',
|
||||||
'resources.worker': 'Worker',
|
'resources.worker': 'Worker',
|
||||||
'resources.modelfiles.form.exsting': 'Downloaded',
|
'resources.modelfiles.form.exsting': 'Downloaded',
|
||||||
'resources.modelfiles.form.added': 'Added'
|
'resources.modelfiles.form.added': 'Added',
|
||||||
|
'resources.worker.maintenance.title': 'System Maintenance',
|
||||||
|
'resources.worker.maintenance.enable': 'Enter Maintenance Mode',
|
||||||
|
'resources.worker.maintenance.disable': 'Exit Maintenance Mode',
|
||||||
|
'resources.worker.maintenance.remark': 'Remark',
|
||||||
|
'resources.worker.maintenance.remark.rules':
|
||||||
|
'Please enter maintenance remarks',
|
||||||
|
'resources.worker.maintenance.tips':
|
||||||
|
'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -117,5 +125,11 @@ export default {
|
|||||||
// 14. 'resources.worker.download.privatekey': 'Download Private Key',
|
// 14. 'resources.worker.download.privatekey': 'Download Private Key',
|
||||||
// 15. 'resources.worker': 'Worker',
|
// 15. 'resources.worker': 'Worker',
|
||||||
// 16. 'resources.modelfiles.form.exsting': 'Downloaded',
|
// 16. 'resources.modelfiles.form.exsting': 'Downloaded',
|
||||||
// 17. 'resources.modelfiles.form.added': 'Added'
|
// 17. 'resources.modelfiles.form.added': 'Added',
|
||||||
|
// 18. 'resources.worker.maintenance.title': 'System Maintenance',
|
||||||
|
// 19. 'resources.worker.maintenance.enable': 'Enter Maintenance Mode',
|
||||||
|
// 20. 'resources.worker.maintenance.disable': 'Exit Maintenance Mode',
|
||||||
|
// 21. 'resources.worker.maintenance.remark': 'Remark',
|
||||||
|
// 22. 'resources.worker.maintenance.remark.rules': 'Please enter maintenance remarks',
|
||||||
|
// 23. 'resources.worker.maintenance.tips': 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -95,9 +95,22 @@ export default {
|
|||||||
'resources.worker.download.privatekey': 'Скачать приватный ключ',
|
'resources.worker.download.privatekey': 'Скачать приватный ключ',
|
||||||
'resources.worker': 'Рабочий узел',
|
'resources.worker': 'Рабочий узел',
|
||||||
'resources.modelfiles.form.exsting': 'Загружено',
|
'resources.modelfiles.form.exsting': 'Загружено',
|
||||||
'resources.modelfiles.form.added': 'Добавлено'
|
'resources.modelfiles.form.added': 'Добавлено',
|
||||||
|
'resources.worker.maintenance.title': 'System Maintenance',
|
||||||
|
'resources.worker.maintenance.enable': 'Enter Maintenance Mode',
|
||||||
|
'resources.worker.maintenance.disable': 'Exit Maintenance Mode',
|
||||||
|
'resources.worker.maintenance.remark': 'Remark',
|
||||||
|
'resources.worker.maintenance.remark.rules':
|
||||||
|
'Please enter maintenance remarks',
|
||||||
|
'resources.worker.maintenance.tips':
|
||||||
|
'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
// 2. 'resources.worker.maintenance.title': 'System Maintenance',
|
||||||
|
// 3. 'resources.worker.maintenance.enable': 'Enter Maintenance Mode',
|
||||||
|
// 4. 'resources.worker.maintenance.disable': 'Exit Maintenance Mode',
|
||||||
|
// 5. 'resources.worker.maintenance.remark': 'Remark',
|
||||||
|
// 6. 'resources.worker.maintenance.remark.rules': 'Please enter maintenance remarks',
|
||||||
|
// 7. 'resources.worker.maintenance.tips': 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -94,5 +94,12 @@ export default {
|
|||||||
'resources.model.instance': '模型实例',
|
'resources.model.instance': '模型实例',
|
||||||
'resources.worker.download.privatekey': '下载私钥',
|
'resources.worker.download.privatekey': '下载私钥',
|
||||||
'resources.modelfiles.form.exsting': '已下载',
|
'resources.modelfiles.form.exsting': '已下载',
|
||||||
'resources.modelfiles.form.added': '已添加'
|
'resources.modelfiles.form.added': '已添加',
|
||||||
|
'resources.worker.maintenance.title': '系统维护',
|
||||||
|
'resources.worker.maintenance.enable': '开启维护',
|
||||||
|
'resources.worker.maintenance.disable': '停止维护',
|
||||||
|
'resources.worker.maintenance.remark': '备注',
|
||||||
|
'resources.worker.maintenance.remark.rules': '请输入维护备注',
|
||||||
|
'resources.worker.maintenance.tips':
|
||||||
|
'启用维护后,节点将停止调度新的模型实例部署任务,正在运行的实例不会受到影响。'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import React from 'react';
|
|||||||
|
|
||||||
type ViewModalProps = {
|
type ViewModalProps = {
|
||||||
currentGPU?: string;
|
currentGPU?: string;
|
||||||
|
workerIP?: string;
|
||||||
registrationInfo: {
|
registrationInfo: {
|
||||||
token: string;
|
token: string;
|
||||||
image: string;
|
image: string;
|
||||||
@@ -13,6 +14,7 @@ type ViewModalProps = {
|
|||||||
|
|
||||||
const AddWorkerCommand: React.FC<ViewModalProps> = ({
|
const AddWorkerCommand: React.FC<ViewModalProps> = ({
|
||||||
registrationInfo,
|
registrationInfo,
|
||||||
|
workerIP,
|
||||||
currentGPU
|
currentGPU
|
||||||
}) => {
|
}) => {
|
||||||
const code = React.useMemo(() => {
|
const code = React.useMemo(() => {
|
||||||
@@ -21,10 +23,11 @@ const AddWorkerCommand: React.FC<ViewModalProps> = ({
|
|||||||
gpu: currentGPU || '',
|
gpu: currentGPU || '',
|
||||||
server: registrationInfo.server_url || origin,
|
server: registrationInfo.server_url || origin,
|
||||||
tag: '',
|
tag: '',
|
||||||
|
workerIP: '${WORKER_IP}',
|
||||||
image: registrationInfo.image,
|
image: registrationInfo.image,
|
||||||
token: registrationInfo.token || '${token}'
|
token: registrationInfo.token || '${token}'
|
||||||
});
|
});
|
||||||
}, [registrationInfo, currentGPU]);
|
}, [registrationInfo, currentGPU, workerIP]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HighlightCode
|
<HighlightCode
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ const AddWorkerStep: React.FC<AddModalProps> = ({
|
|||||||
const [currentGPU, setCurrentGPU] = React.useState<string>(
|
const [currentGPU, setCurrentGPU] = React.useState<string>(
|
||||||
GPUDriverMap.NVIDIA
|
GPUDriverMap.NVIDIA
|
||||||
);
|
);
|
||||||
React.useState<string>(GPUDriverMap.NVIDIA);
|
|
||||||
const [workerCommand, setWorkerCommand] = React.useState<Record<string, any>>(
|
const [workerCommand, setWorkerCommand] = React.useState<Record<string, any>>(
|
||||||
{
|
{
|
||||||
label: 'NVIDIA',
|
label: 'NVIDIA',
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
} from '../apis';
|
} from '../apis';
|
||||||
import { ListItem } from '../config/types';
|
import { ListItem } from '../config/types';
|
||||||
import useWorkerColumns from '../hooks/use-worker-columns';
|
import useWorkerColumns from '../hooks/use-worker-columns';
|
||||||
|
import useWorkerMaintenance from '../hooks/use-worker-maintenance';
|
||||||
import UpdateLabels from './update-labels';
|
import UpdateLabels from './update-labels';
|
||||||
import WorkerDetailModal from './worker-detail-modal';
|
import WorkerDetailModal from './worker-detail-modal';
|
||||||
|
|
||||||
@@ -46,6 +47,8 @@ const Workers: React.FC = () => {
|
|||||||
API: WORKERS_API
|
API: WORKERS_API
|
||||||
});
|
});
|
||||||
const [, setClusterSession] = useAtom(clusterSessionAtom);
|
const [, setClusterSession] = useAtom(clusterSessionAtom);
|
||||||
|
const { MaintenanceModal, handleStopMaintenance, setOpenStatus } =
|
||||||
|
useWorkerMaintenance({ fetchData });
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -154,6 +157,17 @@ const Workers: React.FC = () => {
|
|||||||
name: record.name
|
name: record.name
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (val === 'star_maintenance') {
|
||||||
|
setOpenStatus({
|
||||||
|
open: true,
|
||||||
|
currentData: record
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val === 'stop_maintenance') {
|
||||||
|
handleStopMaintenance(record);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleAddWorker = () => {
|
const handleAddWorker = () => {
|
||||||
@@ -253,6 +267,7 @@ const Workers: React.FC = () => {
|
|||||||
setWorkerDetailStatus({ currentData: null, open: false })
|
setWorkerDetailStatus({ currentData: null, open: false })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{MaintenanceModal}
|
||||||
</PageBox>
|
</PageBox>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -134,25 +134,19 @@ const registerWorker = (params: {
|
|||||||
token: string;
|
token: string;
|
||||||
image: string;
|
image: string;
|
||||||
gpu: string;
|
gpu: string;
|
||||||
|
workerIP?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const config = GPUsConfigs[params.gpu];
|
const config = GPUsConfigs[params.gpu];
|
||||||
return `CONTAINER_NAME="gpustack-worker" \\
|
return `sudo docker run -d --name gpustack-worker \\
|
||||||
WORKER_PORT="80" \\
|
|
||||||
WORKER_IP="" \\
|
|
||||||
sudo docker run -d --name \${CONTAINER_NAME} \\
|
|
||||||
--restart=unless-stopped \\
|
--restart=unless-stopped \\
|
||||||
--privileged \\
|
--privileged \\
|
||||||
--publish \${WORKER_PORT}:\${WORKER_PORT} \\
|
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_DEPLOYMENT=true" \\
|
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_NAME=\${CONTAINER_NAME}" \\
|
|
||||||
--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 gpustack-data:/var/lib/gpustack \\
|
||||||
--runtime ${config.runtime} \\
|
--runtime ${config.runtime} \\
|
||||||
${params.image} \\
|
${params.image} \\
|
||||||
--server-url ${params.server} \\
|
--server-url ${params.server} \\
|
||||||
--token ${params.token} \\
|
--token ${params.token} \\
|
||||||
--worker-port \${WORKER_PORT} \\
|
${params.workerIP ? `--advertise-address ${params.workerIP}` : ''}`.trim();
|
||||||
--advertise-address \${WORKER_IP}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// avaliable for Ascend
|
// avaliable for Ascend
|
||||||
@@ -162,17 +156,13 @@ const registerAscendWorker = (params: {
|
|||||||
token: string;
|
token: string;
|
||||||
image: string;
|
image: string;
|
||||||
gpu: string;
|
gpu: string;
|
||||||
|
workerIP?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const config = GPUsConfigs[params.gpu];
|
const config = GPUsConfigs[params.gpu];
|
||||||
return `CONTAINER_NAME="gpustack-worker" \\
|
return `sudo docker run -d --name gpustack-worker \\
|
||||||
WORKER_PORT="80" \\
|
|
||||||
WORKER_IP="" \\
|
|
||||||
sudo docker run -d --name \${CONTAINER_NAME} \\
|
|
||||||
--restart=unless-stopped \\
|
--restart=unless-stopped \\
|
||||||
--privileged \\
|
--privileged \\
|
||||||
--publish \${WORKER_PORT}:\${WORKER_PORT} \\
|
--network=host \\
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_DEPLOYMENT=true" \\
|
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_NAME=\${CONTAINER_NAME}" \\
|
|
||||||
--env "ASCEND_VISIBLE_DEVICES=$(npu-smi info -m | tail -n 1 | awk '{print $1}') \\
|
--env "ASCEND_VISIBLE_DEVICES=$(npu-smi info -m | tail -n 1 | awk '{print $1}') \\
|
||||||
--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 gpustack-data:/var/lib/gpustack \\
|
||||||
@@ -180,8 +170,7 @@ sudo docker run -d --name \${CONTAINER_NAME} \\
|
|||||||
${params.image} \\
|
${params.image} \\
|
||||||
--server-url ${params.server} \\
|
--server-url ${params.server} \\
|
||||||
--token ${params.token} \\
|
--token ${params.token} \\
|
||||||
--worker-port \${WORKER_PORT} \\
|
${params.workerIP ? `--advertise-address ${params.workerIP}` : ''}`.trim();
|
||||||
--advertise-address \${WORKER_IP}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerHygonWorker = (params: {
|
const registerHygonWorker = (params: {
|
||||||
@@ -190,17 +179,13 @@ const registerHygonWorker = (params: {
|
|||||||
token: string;
|
token: string;
|
||||||
image: string;
|
image: string;
|
||||||
gpu: string;
|
gpu: string;
|
||||||
|
workerIP?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const config = GPUsConfigs[params.gpu];
|
const config = GPUsConfigs[params.gpu];
|
||||||
return `CONTAINER_NAME="gpustack-worker" \\
|
return `sudo docker run -d --name gpustack-worker \\
|
||||||
WORKER_PORT="80" \\
|
|
||||||
WORKER_IP="" \\
|
|
||||||
sudo docker run -d --name \${CONTAINER_NAME} \\
|
|
||||||
--restart=unless-stopped \\
|
--restart=unless-stopped \\
|
||||||
--privileged \\
|
--privileged \\
|
||||||
--publish \${WORKER_PORT}:\${WORKER_PORT} \\
|
--network=host \\
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_DEPLOYMENT=true" \\
|
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_NAME=\${CONTAINER_NAME}" \\
|
|
||||||
--env ROCM_PATH=/opt/dtk \\
|
--env ROCM_PATH=/opt/dtk \\
|
||||||
--env ROCM_SMI_LIB_PATH=/opt/hyhal/lib \\
|
--env ROCM_SMI_LIB_PATH=/opt/hyhal/lib \\
|
||||||
--volume /var/run/docker.sock:/var/run/docker.sock \\
|
--volume /var/run/docker.sock:/var/run/docker.sock \\
|
||||||
@@ -210,8 +195,7 @@ sudo docker run -d --name \${CONTAINER_NAME} \\
|
|||||||
${params.image} \\
|
${params.image} \\
|
||||||
--server-url ${params.server} \\
|
--server-url ${params.server} \\
|
||||||
--token ${params.token} \\
|
--token ${params.token} \\
|
||||||
--worker-port \${WORKER_PORT} \\
|
${params.workerIP ? `--advertise-address ${params.workerIP}` : ''}`.trim();
|
||||||
--advertise-address \${WORKER_IP}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerIluvatarWorker = (params: {
|
const registerIluvatarWorker = (params: {
|
||||||
@@ -220,17 +204,13 @@ const registerIluvatarWorker = (params: {
|
|||||||
token: string;
|
token: string;
|
||||||
image: string;
|
image: string;
|
||||||
gpu: string;
|
gpu: string;
|
||||||
|
workerIP?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const config = GPUsConfigs[params.gpu];
|
const config = GPUsConfigs[params.gpu];
|
||||||
return `CONTAINER_NAME="gpustack-worker" \\
|
return `sudo docker run -d --name gpustack-worker \\
|
||||||
WORKER_PORT="80" \\
|
|
||||||
WORKER_IP="" \\
|
|
||||||
sudo docker run -d --name \${CONTAINER_NAME} \\
|
|
||||||
--restart=unless-stopped \\
|
--restart=unless-stopped \\
|
||||||
--privileged \\
|
--privileged \\
|
||||||
--publish \${WORKER_PORT}:\${WORKER_PORT} \\
|
--network=host \\
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_DEPLOYMENT=true" \\
|
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_NAME=\${CONTAINER_NAME}" \\
|
|
||||||
--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 gpustack-data:/var/lib/gpustack \\
|
||||||
--volume /lib/modules:/lib/modules:ro \\
|
--volume /lib/modules:/lib/modules:ro \\
|
||||||
@@ -240,8 +220,7 @@ sudo docker run -d --name \${CONTAINER_NAME} \\
|
|||||||
${params.image} \\
|
${params.image} \\
|
||||||
--server-url ${params.server} \\
|
--server-url ${params.server} \\
|
||||||
--token ${params.token} \\
|
--token ${params.token} \\
|
||||||
--worker-port \${WORKER_PORT} \\
|
${params.workerIP ? `--advertise-address ${params.workerIP}` : ''}`.trim();
|
||||||
--advertise-address \${WORKER_IP}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerMetaXWorker = (params: {
|
const registerMetaXWorker = (params: {
|
||||||
@@ -250,17 +229,13 @@ const registerMetaXWorker = (params: {
|
|||||||
token: string;
|
token: string;
|
||||||
image: string;
|
image: string;
|
||||||
gpu: string;
|
gpu: string;
|
||||||
|
workerIP?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const config = GPUsConfigs[params.gpu];
|
const config = GPUsConfigs[params.gpu];
|
||||||
return `CONTAINER_NAME="gpustack-worker" \\
|
return `sudo docker run -d --name gpustack-worker \\
|
||||||
WORKER_PORT="80" \\
|
|
||||||
WORKER_IP="" \\
|
|
||||||
sudo docker run -d --name \${CONTAINER_NAME} \\
|
|
||||||
--restart=unless-stopped \\
|
--restart=unless-stopped \\
|
||||||
--privileged \\
|
--privileged \\
|
||||||
--publish \${WORKER_PORT}:\${WORKER_PORT} \\
|
--network=host \\
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_DEPLOYMENT=true" \\
|
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_NAME=\${CONTAINER_NAME}" \\
|
|
||||||
--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 gpustack-data:/var/lib/gpustack \\
|
||||||
--volume /opt/mxdriver:/opt/mxdriver:ro \\
|
--volume /opt/mxdriver:/opt/mxdriver:ro \\
|
||||||
@@ -268,8 +243,7 @@ sudo docker run -d --name \${CONTAINER_NAME} \\
|
|||||||
${params.image} \\
|
${params.image} \\
|
||||||
--server-url ${params.server} \\
|
--server-url ${params.server} \\
|
||||||
--token ${params.token} \\
|
--token ${params.token} \\
|
||||||
--worker-port \${WORKER_PORT} \\
|
${params.workerIP ? `--advertise-address ${params.workerIP}` : ''}`.trim();
|
||||||
--advertise-address \${WORKER_IP}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerCambriconWorker = (params: {
|
const registerCambriconWorker = (params: {
|
||||||
@@ -278,17 +252,13 @@ const registerCambriconWorker = (params: {
|
|||||||
token: string;
|
token: string;
|
||||||
image: string;
|
image: string;
|
||||||
gpu: string;
|
gpu: string;
|
||||||
|
workerIP?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const config = GPUsConfigs[params.gpu];
|
const config = GPUsConfigs[params.gpu];
|
||||||
return `CONTAINER_NAME="gpustack-worker" \\
|
return `sudo docker run -d --name gpustack-worker \\
|
||||||
WORKER_PORT="80" \\
|
|
||||||
WORKER_IP="" \\
|
|
||||||
sudo docker run -d --name \${CONTAINER_NAME} \\
|
|
||||||
--restart=unless-stopped \\
|
--restart=unless-stopped \\
|
||||||
--privileged \\
|
--privileged \\
|
||||||
--publish \${WORKER_PORT}:\${WORKER_PORT} \\
|
--network=host \\
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_DEPLOYMENT=true" \\
|
|
||||||
--env "GPUSTACK_RUNTIME_DEPLOY_MIRRORED_NAME=\${CONTAINER_NAME}" \\
|
|
||||||
--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 gpustack-data:/var/lib/gpustack \\
|
||||||
--volume /usr/local/neuware:/usr/local/neuware:ro \\
|
--volume /usr/local/neuware:/usr/local/neuware:ro \\
|
||||||
@@ -296,8 +266,7 @@ sudo docker run -d --name \${CONTAINER_NAME} \\
|
|||||||
${params.image} \\
|
${params.image} \\
|
||||||
--server-url ${params.server} \\
|
--server-url ${params.server} \\
|
||||||
--token ${params.token} \\
|
--token ${params.token} \\
|
||||||
--worker-port \${WORKER_PORT} \\
|
${params.workerIP ? `--advertise-address ${params.workerIP}` : ''}`.trim();
|
||||||
--advertise-address \${WORKER_IP}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const registerAddWokerCommandMap = {
|
export const registerAddWokerCommandMap = {
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export const WorkerStatusMap = {
|
|||||||
deleting: 'deleting',
|
deleting: 'deleting',
|
||||||
error: 'error',
|
error: 'error',
|
||||||
pending: 'pending',
|
pending: 'pending',
|
||||||
initializing: 'initializing'
|
initializing: 'initializing',
|
||||||
|
maintenance: 'maintenance'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WorkerStatusMapValue = {
|
export const WorkerStatusMapValue = {
|
||||||
@@ -25,7 +26,8 @@ export const WorkerStatusMapValue = {
|
|||||||
[WorkerStatusMap.deleting]: 'Deleting',
|
[WorkerStatusMap.deleting]: 'Deleting',
|
||||||
[WorkerStatusMap.error]: 'Error',
|
[WorkerStatusMap.error]: 'Error',
|
||||||
[WorkerStatusMap.initializing]: 'Initializing',
|
[WorkerStatusMap.initializing]: 'Initializing',
|
||||||
[WorkerStatusMap.pending]: 'Pending'
|
[WorkerStatusMap.pending]: 'Pending',
|
||||||
|
[WorkerStatusMap.maintenance]: 'Maintenance'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const status: any = {
|
export const status: any = {
|
||||||
@@ -36,7 +38,8 @@ export const status: any = {
|
|||||||
[WorkerStatusMap.deleting]: StatusMaps.transitioning,
|
[WorkerStatusMap.deleting]: StatusMaps.transitioning,
|
||||||
[WorkerStatusMap.error]: StatusMaps.error,
|
[WorkerStatusMap.error]: StatusMaps.error,
|
||||||
[WorkerStatusMap.pending]: StatusMaps.transitioning,
|
[WorkerStatusMap.pending]: StatusMaps.transitioning,
|
||||||
[WorkerStatusMap.initializing]: StatusMaps.transitioning
|
[WorkerStatusMap.initializing]: StatusMaps.transitioning,
|
||||||
|
[WorkerStatusMap.maintenance]: StatusMaps.warning
|
||||||
};
|
};
|
||||||
export const checkEnvCommand = (gpu: string) => {
|
export const checkEnvCommand = (gpu: string) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ export interface ListItem {
|
|||||||
state_message: string;
|
state_message: string;
|
||||||
ssh_key_id: string;
|
ssh_key_id: string;
|
||||||
provision_progress: string;
|
provision_progress: string;
|
||||||
|
maintenance: {
|
||||||
|
enabled: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
status: {
|
status: {
|
||||||
cpu: {
|
cpu: {
|
||||||
total: number;
|
total: number;
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import {
|
|||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
DownloadOutlined,
|
DownloadOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
InfoCircleOutlined
|
InfoCircleOutlined,
|
||||||
|
SafetyOutlined,
|
||||||
|
ToolOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Tooltip } from 'antd';
|
import { Tooltip } from 'antd';
|
||||||
@@ -31,6 +33,16 @@ const ActionList = [
|
|||||||
key: 'download_ssh_key',
|
key: 'download_ssh_key',
|
||||||
icon: <DownloadOutlined />
|
icon: <DownloadOutlined />
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'resources.worker.maintenance.enable',
|
||||||
|
key: 'star_maintenance',
|
||||||
|
icon: <ToolOutlined />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'resources.worker.maintenance.disable',
|
||||||
|
key: 'stop_maintenance',
|
||||||
|
icon: <SafetyOutlined />
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// label: 'common.button.logs',
|
// label: 'common.button.logs',
|
||||||
// locale: false,
|
// locale: false,
|
||||||
@@ -51,6 +63,12 @@ const setActions = (row: ListItem) => {
|
|||||||
if (action.key === 'download_ssh_key') {
|
if (action.key === 'download_ssh_key') {
|
||||||
return !!row.ssh_key_id;
|
return !!row.ssh_key_id;
|
||||||
}
|
}
|
||||||
|
if (action.key === 'star_maintenance') {
|
||||||
|
return !row.maintenance?.enabled;
|
||||||
|
}
|
||||||
|
if (action.key === 'stop_maintenance') {
|
||||||
|
return row.maintenance?.enabled;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
import ModalFooter from '@/components/modal-footer';
|
||||||
|
import ScrollerModal from '@/components/scroller-modal';
|
||||||
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import SealTextArea from '@/components/seal-form/seal-textarea';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { updateWorker } from '../apis';
|
||||||
|
import { ListItem } from '../config/types';
|
||||||
|
|
||||||
|
const useWorkerMaintenance = ({ fetchData }: { fetchData: () => void }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [openStatus, setOpenStatus] = React.useState<{
|
||||||
|
open: boolean;
|
||||||
|
currentData: ListItem;
|
||||||
|
}>({
|
||||||
|
open: false,
|
||||||
|
currentData: {} as ListItem
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSumit = () => {
|
||||||
|
form.submit();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
setOpenStatus({
|
||||||
|
open: false,
|
||||||
|
currentData: {} as ListItem
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOk = async (values: any) => {
|
||||||
|
try {
|
||||||
|
await updateWorker(openStatus.currentData.id, {
|
||||||
|
name: openStatus.currentData.name,
|
||||||
|
labels: openStatus.currentData.labels,
|
||||||
|
maintenance: {
|
||||||
|
enabled: true,
|
||||||
|
message: values.remark
|
||||||
|
}
|
||||||
|
});
|
||||||
|
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||||
|
fetchData();
|
||||||
|
onCancel();
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStopMaintenance = async (row: ListItem) => {
|
||||||
|
try {
|
||||||
|
await updateWorker(row.id, {
|
||||||
|
name: row.name,
|
||||||
|
labels: row.labels,
|
||||||
|
maintenance: {
|
||||||
|
enabled: false,
|
||||||
|
message: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||||
|
fetchData();
|
||||||
|
onCancel();
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const MaintenanceModal = (
|
||||||
|
<ScrollerModal
|
||||||
|
title={intl.formatMessage({ id: 'resources.worker.maintenance.title' })}
|
||||||
|
open={openStatus.open}
|
||||||
|
centered={true}
|
||||||
|
onCancel={onCancel}
|
||||||
|
destroyOnHidden={true}
|
||||||
|
closeIcon={true}
|
||||||
|
maskClosable={false}
|
||||||
|
keyboard={false}
|
||||||
|
width={600}
|
||||||
|
maxContentHeight={'max(calc(100vh - 300px), 500px)'}
|
||||||
|
footer={
|
||||||
|
<ModalFooter onOk={handleSumit} onCancel={onCancel}></ModalFooter>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
onFinish={onOk}
|
||||||
|
preserve={false}
|
||||||
|
clearOnDestroy={true}
|
||||||
|
initialValues={{
|
||||||
|
name: openStatus.currentData.name
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form.Item name="name">
|
||||||
|
<SealInput.Input
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'common.table.name'
|
||||||
|
})}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="remark"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: intl.formatMessage({
|
||||||
|
id: 'resources.worker.maintenance.remark.rules'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SealTextArea
|
||||||
|
required
|
||||||
|
alwaysFocus
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'resources.worker.maintenance.remark'
|
||||||
|
})}
|
||||||
|
description={intl.formatMessage({
|
||||||
|
id: 'resources.worker.maintenance.tips'
|
||||||
|
})}
|
||||||
|
autoSize={{ minRows: 4, maxRows: 6 }}
|
||||||
|
></SealTextArea>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</ScrollerModal>
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
MaintenanceModal,
|
||||||
|
handleStopMaintenance,
|
||||||
|
setOpenStatus
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useWorkerMaintenance;
|
||||||
Reference in New Issue
Block a user