diff --git a/src/locales/en-US/gpuservice.ts b/src/locales/en-US/gpuservice.ts
index e7fd2d46..c01190be 100644
--- a/src/locales/en-US/gpuservice.ts
+++ b/src/locales/en-US/gpuservice.ts
@@ -178,9 +178,6 @@ export default {
'Only events from the last hour are shown',
'gpuservice.instance.event.tab.instance': 'Instance Events',
'gpuservice.instance.event.tab.volume': 'Volume Events',
- 'gpuservice.instance.recreate.confirm.title': 'Confirm recreation',
- 'gpuservice.instance.recreate.confirm.content':
- 'The current instance will be deleted first, then recreated with the current configuration.\n {name}',
'gpuservice.storage': 'Storage',
'gpuservice.storage.add': 'Add Storage',
'gpuservice.storage.edit': 'Edit Storage',
diff --git a/src/locales/ja-JP/gpuservice.ts b/src/locales/ja-JP/gpuservice.ts
index eb330dbc..670a23d0 100644
--- a/src/locales/ja-JP/gpuservice.ts
+++ b/src/locales/ja-JP/gpuservice.ts
@@ -177,9 +177,6 @@ export default {
'直近 1 時間のイベントのみ表示されます',
'gpuservice.instance.event.tab.instance': 'インスタンスイベント',
'gpuservice.instance.event.tab.volume': 'ボリュームイベント',
- 'gpuservice.instance.recreate.confirm.title': '再作成を確認しますか',
- 'gpuservice.instance.recreate.confirm.content':
- '現在のインスタンスを削除した後、現在の構成で再作成します。\n {name}',
'gpuservice.storage': 'ストレージ',
'gpuservice.storage.add': 'ストレージを追加',
'gpuservice.storage.edit': 'ストレージを編集',
diff --git a/src/locales/ru-RU/gpuservice.ts b/src/locales/ru-RU/gpuservice.ts
index 2087b850..49135941 100644
--- a/src/locales/ru-RU/gpuservice.ts
+++ b/src/locales/ru-RU/gpuservice.ts
@@ -176,9 +176,6 @@ export default {
'Отображаются только события за последний час',
'gpuservice.instance.event.tab.instance': 'События экземпляра',
'gpuservice.instance.event.tab.volume': 'События тома',
- 'gpuservice.instance.recreate.confirm.title': 'Подтвердить пересоздание',
- 'gpuservice.instance.recreate.confirm.content':
- 'Текущий экземпляр будет сначала удалён, а затем пересоздан с текущей конфигурацией.\n {name}',
'gpuservice.storage': 'Хранилище',
'gpuservice.storage.add': 'Добавить хранилище',
'gpuservice.storage.edit': 'Редактировать хранилище',
diff --git a/src/locales/tr-TR/gpuservice.ts b/src/locales/tr-TR/gpuservice.ts
index 62a0199e..a427b17d 100644
--- a/src/locales/tr-TR/gpuservice.ts
+++ b/src/locales/tr-TR/gpuservice.ts
@@ -172,10 +172,6 @@ export default {
'Yalnızca son bir saatteki olaylar gösterilir',
'gpuservice.instance.event.tab.instance': 'Örnek Olayları',
'gpuservice.instance.event.tab.volume': 'Birim Olayları',
- 'gpuservice.instance.recreate.confirm.title':
- 'Yeniden oluşturma onaylansın mı',
- 'gpuservice.instance.recreate.confirm.content':
- 'Mevcut örnek önce silinecek, ardından mevcut yapılandırmayla yeniden oluşturulacaktır.\n {name}',
'gpuservice.storage': 'Depolama',
'gpuservice.storage.add': 'Depolama Ekle',
'gpuservice.storage.edit': 'Depolamayı Düzenle',
diff --git a/src/locales/zh-CN/gpuservice.ts b/src/locales/zh-CN/gpuservice.ts
index 1de8ff2c..e2e28a0a 100644
--- a/src/locales/zh-CN/gpuservice.ts
+++ b/src/locales/zh-CN/gpuservice.ts
@@ -162,9 +162,6 @@ export default {
'gpuservice.instance.event.recentHourTip': '仅显示最近一小时的事件。',
'gpuservice.instance.event.tab.instance': '实例事件',
'gpuservice.instance.event.tab.volume': '存储卷事件',
- 'gpuservice.instance.recreate.confirm.title': '确认重新创建',
- 'gpuservice.instance.recreate.confirm.content':
- '系统将先删除当前实例,然后使用当前配置重新创建。\n {name}',
'gpuservice.storage': '存储',
'gpuservice.storage.add': '添加存储',
'gpuservice.storage.edit': '编辑存储',
diff --git a/src/pages/gpu-service/instances/components/add-modal.tsx b/src/pages/gpu-service/instances/components/add-modal.tsx
index ac91e9ef..ea3e69b5 100644
--- a/src/pages/gpu-service/instances/components/add-modal.tsx
+++ b/src/pages/gpu-service/instances/components/add-modal.tsx
@@ -5,12 +5,7 @@ import useUserDirectory from '@/pages/gpu-service/hooks/use-user-directory';
import Separator from '@/pages/llmodels/components/separator';
import { getGPUStackPlugin } from '@/plugins';
import { SearchOutlined } from '@ant-design/icons';
-import {
- AlertBlockInfo,
- ColumnWrapper,
- GSDrawer,
- ModalFooter
-} from '@gpustack/core-ui';
+import { ColumnWrapper, GSDrawer, ModalFooter } from '@gpustack/core-ui';
import { useIntl, useModel } from '@umijs/max';
import { Input, Typography } from 'antd';
import { useEffect, useMemo, useRef, useState } from 'react';
@@ -30,7 +25,6 @@ type AddModalProps = {
action: PageActionType;
open: boolean;
width?: number | string;
- realAction?: string;
clusterList?: Array<{
label: string;
value: number;
@@ -84,8 +78,7 @@ const AddModal: React.FC = ({
data,
onCancel,
width,
- clusterList = [],
- realAction
+ clusterList = []
}) => {
const intl = useIntl();
const { initialState } = useModel('@@initialState') || {};
@@ -173,16 +166,18 @@ const AddModal: React.FC = ({
);
// const readonly = action === PageAction.VIEW;
const readonly = false;
- const isRecreate = realAction === PageAction.CREATE;
- const showResourceSelectors = action === PageAction.CREATE || isRecreate;
- const shouldAutoSelectResource = action === PageAction.CREATE && !isRecreate;
+ const showResourceSelectors = action === PageAction.CREATE;
// Only a stopped instance can be re-typed on edit. It shows the instance-type
// column (but not the template column) beside the form; the create card
- // columns render for CREATE / recreate.
+ // columns render for CREATE.
const isStoppedEdit =
action === PageAction.EDIT &&
data?.status?.phase === InstanceStatusValueMap.Stopped;
const showInstanceTypeColumn = showResourceSelectors || isStoppedEdit;
+ // Editing a non-stopped instance is restricted: only displayName and the
+ // SSH public keys stay editable; the type / template / storage sections
+ // render disabled. A stopped instance edits everything.
+ const isRestrictedEdit = action === PageAction.EDIT && !isStoppedEdit;
const findTemplateByManufacturer = (
manufacturer: string | undefined,
@@ -266,43 +261,12 @@ const AddModal: React.FC = ({
);
};
- const findAggregateOf = (
- candidateName: string | undefined,
- clusterId: number | null | undefined,
- instanceTypes: InstanceTypeItem[]
- ): InstanceTypeItem | undefined => {
- if (!candidateName) return undefined;
- return instanceTypes.find((item) =>
- (item.status?.tiers ?? []).some((tier) =>
- (tier.candidates ?? []).some(
- (c) => c.name === candidateName && Number(c.cluster) === clusterId
- )
- )
- );
- };
-
// initial for first
const applyAutoSelection = (
instanceTypes: InstanceTypeItem[],
templates: TemplateItem[],
orgId?: number | null
) => {
- // On edit / view, surface the persisted selection in the card list.
- if (!shouldAutoSelectResource) {
- const aggregate = findAggregateOf(
- data?.spec?.type,
- data?.clusterId,
- instanceTypes
- );
- if (aggregate) {
- setInstanceTypeSelection({
- instanceType: aggregate.name,
- manufacturer: manufacturerOf(aggregate)
- });
- }
- return;
- }
-
// Scope to clusters the chosen org owns (admin "All" view).
const owned = filterTypesByOwner(instanceTypes, orgId);
@@ -376,7 +340,7 @@ const AddModal: React.FC = ({
// full instance-type list to re-type a stopped instance.
fetchData({ page: -1 });
}
- }, [open, shouldAutoSelectResource, action]);
+ }, [open, action]);
// filter instance types (already scoped to the chosen org's clusters)
const filteredInstanceTypes = ownedInstanceTypes.filter((item) =>
@@ -664,17 +628,6 @@ const AddModal: React.FC = ({
styles={{ container: { paddingBlock: 0 } }}
footer={
<>
- {isRecreate && open && (
-
- )}
= ({
= forwardRef(
(props, ref) => {
const {
action,
- realAction,
currentData,
disabled,
+ restrictedEdit,
open,
instanceTypeList = [],
noAvailableInstanceTypes,
@@ -125,8 +128,9 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
const { getRuleMessage } = useAppUtils();
const [form] = Form.useForm();
const scrollTabsRef = useRef(null);
- const formAction =
- realAction === PageAction.CREATE ? PageAction.CREATE : action;
+ // Restricted (non-stopped) edit disables the type / template / storage
+ // sections; displayName and the SSH public keys keep following `disabled`.
+ const sectionDisabled = disabled || restrictedEdit;
const sshEnabled = Form.useWatch('enable_ssh', form);
const description = Form.useWatch(['description'], form);
// `organization_id` is owned by the create-scope picker slot; it only
@@ -464,8 +468,8 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
};
// Whole-card (exclusive) vs sliced (percentage) mode. Only meaningful for
- // sliceable accelerator types; derived (no persisted field) — on edit/
- // recreate it is inferred from acceleratorSlicedMemoryPercentage > 0.
+ // sliceable accelerator types; derived (no persisted field) — on edit it
+ // is inferred from acceleratorSlicedMemoryPercentage > 0.
const [sliceMode, setSliceMode] = useState<'whole' | 'sliced'>('whole');
const handleAcceleratorChange = (count: number) => {
@@ -582,11 +586,8 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
return;
}
- if (
- action === PageAction.EDIT ||
- action === PageAction.VIEW ||
- realAction === PageAction.CREATE
- ) {
+ // Prefill from the source row on edit / view.
+ if (currentData) {
console.log('currentData', currentData);
const currentSpec = parseJsonSafe(
currentData?.description || '{}',
@@ -597,8 +598,8 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
? _.toNumber(currentData?.spec?.resources?.accelerator)
: _.toNumber(currentData?.spec?.resources?.cpu) || 0;
- // Infer the mode from the persisted slice percentage (recreate keeps
- // the section editable; edit/view render a readonly card).
+ // Infer the mode from the persisted slice percentage (edit/view
+ // render a readonly card).
const persistedSliced =
_.toNumber(
currentData?.spec?.resources?.acceleratorSlicedMemoryPercentage
@@ -624,12 +625,12 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
});
// buildResourcesData above filled CPU / RAM for the whole card; rescale
- // them off the persisted percentages when recreating a sliced instance.
+ // them off the persisted percentages for a sliced instance.
if (persistedSliced) {
applySlicedResourceScaling();
}
}
- }, [action, currentData, form, open, realAction, instanceTypeList]);
+ }, [action, currentData, form, open, instanceTypeList]);
const getUnitResources = () => {
if (selectedInstanceType?.spec?.unitResourcesParsed) {
@@ -736,7 +737,7 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
>
= forwardRef(
storageMode: StorageModeValueMap.Temporary
}}
>
-
+
@@ -797,8 +798,8 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
forceRender: true,
children: (
= forwardRef(
children: (
)
@@ -832,7 +833,7 @@ const GPUServiceInstanceForm: React.FC = forwardRef(
}),
forceRender: true,
children: (
-
+
)
}
]}
diff --git a/src/pages/gpu-service/instances/forms/instance-type.tsx b/src/pages/gpu-service/instances/forms/instance-type.tsx
index b06ee7f2..cff90506 100644
--- a/src/pages/gpu-service/instances/forms/instance-type.tsx
+++ b/src/pages/gpu-service/instances/forms/instance-type.tsx
@@ -136,8 +136,8 @@ const InstanceTypeFormItem: React.FC = ({
};
// Sliced mode is only offered for sliceable accelerator types, and only when
- // the section is editable (create / recreate, or edit after re-picking a
- // type; a not-yet-re-typed edit renders a readonly card).
+ // the section is editable (create, or edit after re-picking a type; a
+ // not-yet-re-typed edit renders a readonly card).
const showModeSwitch =
!readonlyType &&
isGPUType &&
diff --git a/src/pages/gpu-service/instances/hooks/use-create-instance.ts b/src/pages/gpu-service/instances/hooks/use-create-instance.ts
index dcac55fe..775841a8 100644
--- a/src/pages/gpu-service/instances/hooks/use-create-instance.ts
+++ b/src/pages/gpu-service/instances/hooks/use-create-instance.ts
@@ -15,30 +15,26 @@ const useCreateInstance = () => {
title: string;
currentData?: ListItem | null;
width?: number | string;
- realAction?: string;
}>({
action: PageAction.CREATE,
title: '',
open: false,
width: undefined,
- currentData: null,
- realAction: undefined
+ currentData: null
});
const openModal = (
action: PageActionType,
title: string,
currentData?: ListItem | null,
- width?: number | string,
- realAction?: string
+ width?: number | string
) => {
setOpenModalStatus({
action,
title,
open: true,
currentData,
- width,
- realAction
+ width
});
saveScrollHeight();
};
@@ -73,23 +69,12 @@ const useCreateInstance = () => {
);
};
- const openRecreateInstanceModal = (row: ListItem) => {
- openModal(
- PageAction.EDIT,
- intl.formatMessage({ id: 'common.button.recreate' }),
- row,
- 'calc(100vw - 220px)',
- PageAction.CREATE
- );
- };
-
const closeModal = () => {
setOpenModalStatus({
...openModalStatus,
title: '',
open: false,
- currentData: null,
- realAction: undefined
+ currentData: null
});
restoreScrollHeight();
};
@@ -101,7 +86,6 @@ const useCreateInstance = () => {
openCreateInstanceModal,
openEditInstanceModal,
openViewInstanceModal,
- openRecreateInstanceModal,
closeInstanceModal: closeModal
};
};
diff --git a/src/pages/gpu-service/instances/index.tsx b/src/pages/gpu-service/instances/index.tsx
index 51fcf583..54390c04 100644
--- a/src/pages/gpu-service/instances/index.tsx
+++ b/src/pages/gpu-service/instances/index.tsx
@@ -86,7 +86,6 @@ const GPUService: React.FC = () => {
openCreateInstanceModal,
openEditInstanceModal,
openViewInstanceModal,
- openRecreateInstanceModal,
closeInstanceModal
} = useCreateInstance();
const { openViewLogsModal, closeViewLogsModal, openViewLogsModalStatus } =
@@ -136,13 +135,7 @@ const GPUService: React.FC = () => {
const handleModalOk = async (data: FormData) => {
try {
- if (openInstanceModalStatus.realAction === PageAction.CREATE) {
- await deleteGPUServiceInstance(openInstanceModalStatus.currentData!.id);
- await new Promise((resolve) => {
- setTimeout(resolve, 300);
- });
- await createInstance({ data });
- } else if (openInstanceModalStatus.action === PageAction.EDIT) {
+ if (openInstanceModalStatus.action === PageAction.EDIT) {
await updateInstance({
id: openInstanceModalStatus.currentData!.id,
data
@@ -243,8 +236,6 @@ const GPUService: React.FC = () => {
openEditInstanceModal(row);
} else if (val === 'delete') {
handleDelete({ ...row });
- } else if (val === 'recreate') {
- openRecreateInstanceModal(row);
} else if (val === 'viewlog') {
openViewLogsModal(row);
} else if (val === 'viewevent') {
@@ -391,7 +382,6 @@ const GPUService: React.FC = () => {
title={openInstanceModalStatus.title}
data={openInstanceModalStatus.currentData}
width={openInstanceModalStatus.width}
- realAction={openInstanceModalStatus.realAction}
clusterList={clusterList}
onCancel={closeInstanceModal}
onOk={handleModalOk}
diff --git a/src/pages/gpu-service/instances/utils/instance-description.ts b/src/pages/gpu-service/instances/utils/instance-description.ts
index 43c9bf24..0e293abd 100644
--- a/src/pages/gpu-service/instances/utils/instance-description.ts
+++ b/src/pages/gpu-service/instances/utils/instance-description.ts
@@ -23,8 +23,8 @@ export const buildInstanceTypeSnapshotSpec = (
// Serialize the chosen instance type into the instance's `description` field —
// a persisted spec snapshot the form reads back to render the type card and
-// derive unit resources. Shared by the create/recreate flow (card selection)
-// and the edit flow (change-type overlay).
+// derive unit resources. Shared by the create flow (card selection) and the
+// edit flow (change-type overlay).
export const saveInstanceDataInDescription = (
instanceType: InstanceTypeItem
): string => {