diff --git a/src/locales/en-US/gpuservice.ts b/src/locales/en-US/gpuservice.ts
index a872db0f..bdbfa9f5 100644
--- a/src/locales/en-US/gpuservice.ts
+++ b/src/locales/en-US/gpuservice.ts
@@ -134,12 +134,16 @@ export default {
'gpuservice.instance.ram': 'RAM',
'gpuservice.instance.os': 'OS',
'gpuservice.instance.arch': 'Arch',
-'gpuservice.instanceType': 'GPU Instance Type',
+ 'gpuservice.instanceType': 'GPU Instance Type',
'gpuservice.instanceType.add': 'Add Instance Type',
'gpuservice.instanceType.flavor': 'Flavor',
- 'gpuservice.instanceType.flavor.required': 'Please select an instance type flavor',
+ 'gpuservice.instanceType.flavor.required':
+ 'Please select an instance type flavor',
'gpuservice.instanceType.flavor.gpuGroup': 'GPU Compute',
'gpuservice.instanceType.flavor.cpuGroup': 'CPU Compute',
+ 'gpuservice.instanceType.activate': 'Activate',
+ 'gpuservice.instanceType.deactivate': 'Deactivate',
+ 'gpuservice.instanceType.platform': 'Platform',
'gpuservice.instanceType.product': 'Product',
'gpuservice.instanceType.unitCpu': 'Unit CPU',
'gpuservice.instanceType.unitCpu.tip': 'CPU allocated per GPU',
diff --git a/src/locales/ja-JP/gpuservice.ts b/src/locales/ja-JP/gpuservice.ts
index 09d75d51..35dd6d5e 100644
--- a/src/locales/ja-JP/gpuservice.ts
+++ b/src/locales/ja-JP/gpuservice.ts
@@ -133,12 +133,16 @@ export default {
'gpuservice.instance.ram': 'RAM',
'gpuservice.instance.os': 'OS',
'gpuservice.instance.arch': 'アーキテクチャ',
-'gpuservice.instanceType': 'GPU Instance Type',
+ 'gpuservice.instanceType': 'GPU Instance Type',
'gpuservice.instanceType.add': 'Add Instance Type',
'gpuservice.instanceType.flavor': 'Flavor',
- 'gpuservice.instanceType.flavor.required': 'Please select an instance type flavor',
+ 'gpuservice.instanceType.flavor.required':
+ 'Please select an instance type flavor',
'gpuservice.instanceType.flavor.gpuGroup': 'GPU Compute',
'gpuservice.instanceType.flavor.cpuGroup': 'CPU Compute',
+ 'gpuservice.instanceType.activate': 'Activate',
+ 'gpuservice.instanceType.deactivate': 'Deactivate',
+ 'gpuservice.instanceType.platform': 'Platform',
'gpuservice.instanceType.product': 'Product',
'gpuservice.instanceType.unitCpu': 'Unit CPU',
'gpuservice.instanceType.unitCpu.tip': 'CPU allocated per GPU',
diff --git a/src/locales/ru-RU/gpuservice.ts b/src/locales/ru-RU/gpuservice.ts
index 43c96d13..3600069d 100644
--- a/src/locales/ru-RU/gpuservice.ts
+++ b/src/locales/ru-RU/gpuservice.ts
@@ -132,12 +132,16 @@ export default {
'gpuservice.instance.ram': 'RAM',
'gpuservice.instance.os': 'ОС',
'gpuservice.instance.arch': 'Архитектура',
-'gpuservice.instanceType': 'GPU Instance Type',
+ 'gpuservice.instanceType': 'GPU Instance Type',
'gpuservice.instanceType.add': 'Add Instance Type',
'gpuservice.instanceType.flavor': 'Flavor',
- 'gpuservice.instanceType.flavor.required': 'Please select an instance type flavor',
+ 'gpuservice.instanceType.flavor.required':
+ 'Please select an instance type flavor',
'gpuservice.instanceType.flavor.gpuGroup': 'GPU Compute',
'gpuservice.instanceType.flavor.cpuGroup': 'CPU Compute',
+ 'gpuservice.instanceType.activate': 'Activate',
+ 'gpuservice.instanceType.deactivate': 'Deactivate',
+ 'gpuservice.instanceType.platform': 'Platform',
'gpuservice.instanceType.product': 'Product',
'gpuservice.instanceType.unitCpu': 'Unit CPU',
'gpuservice.instanceType.unitCpu.tip': 'CPU allocated per GPU',
diff --git a/src/locales/tr-TR/gpuservice.ts b/src/locales/tr-TR/gpuservice.ts
index 68633202..56dc1911 100644
--- a/src/locales/tr-TR/gpuservice.ts
+++ b/src/locales/tr-TR/gpuservice.ts
@@ -128,12 +128,16 @@ export default {
'gpuservice.instance.ram': 'RAM',
'gpuservice.instance.os': 'OS',
'gpuservice.instance.arch': 'Mimari',
-'gpuservice.instanceType': 'GPU Instance Type',
+ 'gpuservice.instanceType': 'GPU Instance Type',
'gpuservice.instanceType.add': 'Add Instance Type',
'gpuservice.instanceType.flavor': 'Flavor',
- 'gpuservice.instanceType.flavor.required': 'Please select an instance type flavor',
+ 'gpuservice.instanceType.flavor.required':
+ 'Please select an instance type flavor',
'gpuservice.instanceType.flavor.gpuGroup': 'GPU Compute',
'gpuservice.instanceType.flavor.cpuGroup': 'CPU Compute',
+ 'gpuservice.instanceType.activate': 'Activate',
+ 'gpuservice.instanceType.deactivate': 'Deactivate',
+ 'gpuservice.instanceType.platform': 'Platform',
'gpuservice.instanceType.product': 'Product',
'gpuservice.instanceType.unitCpu': 'Unit CPU',
'gpuservice.instanceType.unitCpu.tip': 'CPU allocated per GPU',
diff --git a/src/locales/zh-CN/gpuservice.ts b/src/locales/zh-CN/gpuservice.ts
index bc735d7e..56282404 100644
--- a/src/locales/zh-CN/gpuservice.ts
+++ b/src/locales/zh-CN/gpuservice.ts
@@ -121,12 +121,15 @@ export default {
'gpuservice.instance.ram': '内存',
'gpuservice.instance.os': '系统',
'gpuservice.instance.arch': '架构',
-'gpuservice.instanceType': 'GPU 实例类型',
+ 'gpuservice.instanceType': 'GPU 实例类型',
'gpuservice.instanceType.add': '添加实例类型',
'gpuservice.instanceType.flavor': '规格',
'gpuservice.instanceType.flavor.required': '请选择实例类型规格',
'gpuservice.instanceType.flavor.gpuGroup': 'GPU 算力',
'gpuservice.instanceType.flavor.cpuGroup': 'CPU 算力',
+ 'gpuservice.instanceType.activate': '启用',
+ 'gpuservice.instanceType.deactivate': '停用',
+ 'gpuservice.instanceType.platform': '平台',
'gpuservice.instanceType.product': '商品',
'gpuservice.instanceType.unitCpu': '单位 CPU',
'gpuservice.instanceType.unitCpu.tip': '每 GPU 对应多少 CPU',
diff --git a/src/pages/gpu-service/instance-types/apis/index.ts b/src/pages/gpu-service/instance-types/apis/index.ts
index 868a335d..10b413ef 100644
--- a/src/pages/gpu-service/instance-types/apis/index.ts
+++ b/src/pages/gpu-service/instance-types/apis/index.ts
@@ -52,3 +52,25 @@ export async function deleteGPUInstanceType(params: {
params: { cluster_id: params.cluster_id }
});
}
+
+// PUT /gpu-instance-types/{name}/enactive?cluster_id — activate an instance type.
+export async function activateGPUInstanceType(params: {
+ name: string;
+ cluster_id: number;
+}) {
+ return request(`${GPU_INSTANCE_TYPES_API}/${params.name}/activate`, {
+ method: 'PUT',
+ params: { cluster_id: params.cluster_id }
+ });
+}
+
+// PUT /gpu-instance-types/{name}/deactive?cluster_id — deactivate an instance type.
+export async function deactivateGPUInstanceType(params: {
+ name: string;
+ cluster_id: number;
+}) {
+ return request(`${GPU_INSTANCE_TYPES_API}/${params.name}/deactivate`, {
+ method: 'PUT',
+ params: { cluster_id: params.cluster_id }
+ });
+}
diff --git a/src/pages/gpu-service/instance-types/components/flavor-display.tsx b/src/pages/gpu-service/instance-types/components/flavor-display.tsx
new file mode 100644
index 00000000..0b27c814
--- /dev/null
+++ b/src/pages/gpu-service/instance-types/components/flavor-display.tsx
@@ -0,0 +1,128 @@
+import { AutoTooltip, ThemeTag } from '@gpustack/core-ui';
+import { useIntl } from '@umijs/max';
+import { Flex } from 'antd';
+import { formatMemoryDisplay } from '../../instances/config';
+import { manufactureColorMap } from '../../templates/config';
+import { formatManufacturer } from '../../utils';
+
+// The subset of a flavor / instance-type spec the flavor display reads. Both
+// FlavorItem.spec and InstanceTypeSpec structurally satisfy it, so the create
+// drawer's dropdown and the management list share the same renderers.
+interface FlavorSpecLike {
+ manufacturer?: string | null;
+ product?: string | null;
+ memory?: string | null;
+ sliceable?: boolean;
+ acceleratable?: boolean;
+}
+
+// A flavor's title mirrors the flavor card: a generic (no product, no/`generic`
+// manufacturer, non-acceleratable) flavor reads as "CPU-only".
+export const getFlavorTitle = (
+ spec: FlavorSpecLike = {},
+ fallbackName?: string | null
+) => {
+ const manufacturer = spec.manufacturer || '';
+ const isCpuOnly =
+ !spec.acceleratable &&
+ !spec.product &&
+ (!manufacturer || manufacturer.toLowerCase() === 'generic');
+ return isCpuOnly ? 'CPU-only' : spec.product || fallbackName || '-';
+};
+
+// Secondary line, dot-separated: manufacturer · memory · sliceable. memory and
+// sliceable apply to accelerator (GPU) flavors only; sliceable stays a tag.
+// Returns null when a (generic) flavor has nothing to show.
+export const FlavorMeta: React.FC<{ spec?: FlavorSpecLike }> = ({
+ spec = {}
+}) => {
+ const intl = useIntl();
+ const manufacturer = spec.manufacturer || '';
+ const color = manufactureColorMap[manufacturer] ?? 'purple';
+ const memory = spec.acceleratable
+ ? formatMemoryDisplay(spec.memory ?? undefined)
+ : '';
+
+ const pieces: React.ReactNode[] = [];
+ if (manufacturer) {
+ pieces.push(
+
+ {formatManufacturer(manufacturer)}
+
+ );
+ }
+ if (memory) {
+ pieces.push({memory});
+ }
+ if (spec.acceleratable && spec.sliceable) {
+ pieces.push(
+
+ {intl.formatMessage({ id: 'gpuservice.instance.sliceable' })}
+
+ );
+ }
+ if (!pieces.length) return null;
+
+ return (
+
+ {pieces.flatMap((piece, index) =>
+ index === 0
+ ? [piece]
+ : [
+
+ ·
+ ,
+ piece
+ ]
+ )}
+
+ );
+};
+
+// Two-line flavor display: title on top, meta row below. Shared by the create
+// drawer's dropdown option and the management list's flavor cell.
+export const FlavorOption: React.FC<{
+ spec?: FlavorSpecLike;
+ fallbackName?: string | null;
+ maxWidth?: number | string;
+}> = ({ spec = {}, fallbackName, maxWidth = '100%' }) => (
+
+
+ {getFlavorTitle(spec, fallbackName)}
+
+
+
+);
+
+// Single-line flavor display: title then meta inline. Used for the collapsed
+// selected value in the create drawer's Select.
+export const FlavorSelected: React.FC<{
+ spec?: FlavorSpecLike;
+ fallbackName?: string | null;
+}> = ({ spec = {}, fallbackName }) => (
+
+
+ {getFlavorTitle(spec, fallbackName)}
+
+
+
+);
diff --git a/src/pages/gpu-service/instance-types/config/index.ts b/src/pages/gpu-service/instance-types/config/index.ts
index 040d4c9e..8ccd9389 100644
--- a/src/pages/gpu-service/instance-types/config/index.ts
+++ b/src/pages/gpu-service/instance-types/config/index.ts
@@ -8,24 +8,24 @@ export const GPU_INSTANCE_TYPE_OS = 'linux';
export const InstanceTypePhaseValueMap = {
Active: 'Active',
Inactive: 'Inactive',
- Preparing: 'Preparing'
+ Draining: 'Draining'
};
export const InstanceTypePhaseLabelMap: Record = {
[InstanceTypePhaseValueMap.Active]: 'Active',
[InstanceTypePhaseValueMap.Inactive]: 'Inactive',
- [InstanceTypePhaseValueMap.Preparing]: 'Preparing'
+ [InstanceTypePhaseValueMap.Draining]: 'Draining'
};
export const status: Record = {
[InstanceTypePhaseValueMap.Active]: StatusMaps.success,
[InstanceTypePhaseValueMap.Inactive]: StatusMaps.inactive,
- [InstanceTypePhaseValueMap.Preparing]: StatusMaps.transitioning
+ [InstanceTypePhaseValueMap.Draining]: StatusMaps.transitioning
};
export const ArchOptions = [
- { label: 'amd64', value: 'amd64' },
- { label: 'arm64', value: 'arm64' }
+ { label: 'AMD64', value: 'amd64' },
+ { label: 'ARM64', value: 'arm64' }
];
// ``icon`` is narrowed to ``any`` so the inferred type doesn't reach into
diff --git a/src/pages/gpu-service/instance-types/config/types.ts b/src/pages/gpu-service/instance-types/config/types.ts
index 3961046d..0db754b8 100644
--- a/src/pages/gpu-service/instance-types/config/types.ts
+++ b/src/pages/gpu-service/instance-types/config/types.ts
@@ -4,6 +4,7 @@ export interface UnitResources {
}
export interface InstanceTypeSpec {
+ displayName?: string | null;
manufacturer?: string | null;
product?: string | null;
family?: string | null;
@@ -54,6 +55,7 @@ export interface FlavorItem {
export interface FormData {
name: string;
spec: {
+ displayName?: string | null;
acceleratorGroup?: string | null;
generalGroup?: string | null;
acceleratable?: boolean;
diff --git a/src/pages/gpu-service/instance-types/forms/index.tsx b/src/pages/gpu-service/instance-types/forms/index.tsx
index 1e39a34f..16d5191f 100644
--- a/src/pages/gpu-service/instance-types/forms/index.tsx
+++ b/src/pages/gpu-service/instance-types/forms/index.tsx
@@ -1,18 +1,18 @@
import { validateLabelNameRegxFor63 } from '@/config';
import {
- AutoTooltip,
Input as CInput,
InputNumber,
Select as SealSelect,
- ThemeTag,
useAppUtils
} from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
-import { Flex, Form } from 'antd';
+import { Form } from 'antd';
import { forwardRef, useEffect, useImperativeHandle } from 'react';
-import { formatMemoryDisplay } from '../../instances/config';
-import { manufactureColorMap } from '../../templates/config';
-import { formatManufacturer } from '../../utils';
+import {
+ FlavorOption,
+ FlavorSelected,
+ getFlavorTitle
+} from '../components/flavor-display';
import { ArchOptions, GPU_INSTANCE_TYPE_OS } from '../config';
import { FlavorItem, FormData } from '../config/types';
import styles from '../styles/instance-types.module.less';
@@ -39,18 +39,6 @@ interface InstanceTypeFormProps {
onFinishFailed?: (errorInfo: any) => void;
}
-// A flavor's title mirrors the flavor card: a generic (no product, no/`generic`
-// manufacturer, non-acceleratable) flavor reads as "CPU-only".
-const getFlavorTitle = (flavor: FlavorItem) => {
- const spec = flavor.spec || {};
- const manufacturer = spec.manufacturer || '';
- const isCpuOnly =
- !spec.acceleratable &&
- !spec.product &&
- (!manufacturer || manufacturer.toLowerCase() === 'generic');
- return isCpuOnly ? 'CPU-only' : spec.product || flavor.name || '-';
-};
-
const GPUServiceInstanceTypeForm: React.FC = forwardRef(
(props, ref) => {
const {
@@ -97,102 +85,19 @@ const GPUServiceInstanceTypeForm: React.FC = forwardRef(
}
}));
- // A flavor's dropdown / selected label shows the same info as the flavor
- // Secondary line, dot-separated: manufacturer · memory · sliceable. memory
- // and sliceable apply to accelerator (GPU) flavors only; sliceable stays a
- // tag. Returns null when a (generic) flavor has nothing to show.
- const renderFlavorMeta = (flavor: FlavorItem) => {
- const spec = flavor.spec || {};
- const manufacturer = spec.manufacturer || '';
- const color = manufactureColorMap[manufacturer] ?? 'purple';
- const memory = spec.acceleratable
- ? formatMemoryDisplay(spec.memory ?? undefined)
- : '';
-
- const pieces: React.ReactNode[] = [];
- if (manufacturer) {
- pieces.push(
-
- {formatManufacturer(manufacturer)}
-
- );
- }
- if (memory) {
- pieces.push({memory});
- }
- if (spec.acceleratable && spec.sliceable) {
- pieces.push(
-
- {intl.formatMessage({ id: 'gpuservice.instance.sliceable' })}
-
- );
- }
- if (!pieces.length) return null;
-
- return (
-
- {pieces.flatMap((piece, index) =>
- index === 0
- ? [piece]
- : [
-
- ·
- ,
- piece
- ]
- )}
-
- );
- };
-
- // Dropdown item: two lines — name on top, meta row below.
- const renderFlavorOption = (flavor: FlavorItem) => (
-
-
- {getFlavorTitle(flavor)}
-
- {renderFlavorMeta(flavor)}
-
- );
-
- // Collapsed selected value: single line — name then meta inline.
- const renderFlavorSelected = (flavor: FlavorItem) => (
-
-
- {getFlavorTitle(flavor)}
-
- {renderFlavorMeta(flavor)}
-
- );
-
// Split flavors into two groups: CPU compute (generic) and GPU compute
// (accelerator). Groups render as labeled sections in the dropdown.
const toFlavorOption = (flavor: FlavorItem) => ({
value: flavor.name,
- label: getFlavorTitle(flavor),
+ label: getFlavorTitle(flavor.spec, flavor.name),
flavor
});
- const cpuFlavors = flavorList.filter((flavor) => !flavor.spec?.acceleratable);
- const gpuFlavors = flavorList.filter((flavor) => flavor.spec?.acceleratable);
+ const cpuFlavors = flavorList.filter(
+ (flavor) => !flavor.spec?.acceleratable
+ );
+ const gpuFlavors = flavorList.filter(
+ (flavor) => flavor.spec?.acceleratable
+ );
const flavorOptions = [
cpuFlavors.length && {
label: intl.formatMessage({
@@ -218,6 +123,7 @@ const GPUServiceInstanceTypeForm: React.FC = forwardRef(
await onFinish({
name: values.name,
spec: {
+ displayName: values.spec?.displayName?.trim() || null,
acceleratorGroup: selectedFlavor?.spec?.acceleratorGroup ?? null,
generalGroup: selectedFlavor?.spec?.generalGroup ?? null,
acceleratable: selectedFlavor?.spec?.acceleratable ?? false,
@@ -259,6 +165,25 @@ const GPUServiceInstanceTypeForm: React.FC = forwardRef(
/>
+
+ name={['spec', 'displayName']}
+ rules={[
+ {
+ max: 63,
+ message: intl.formatMessage({
+ id: 'gpuservice.template.displayName.max'
+ })
+ }
+ ]}
+ >
+
+
+
= forwardRef(
flavorList.find((flavor) => flavor.name === val) ?? null
)
}
- optionRender={(option: any) =>
- renderFlavorOption(option.data.flavor)
- }
+ optionRender={(option: any) => {
+ const flavor: FlavorItem = option.data.flavor;
+ return (
+
+ );
+ }}
labelRender={({ value }) => {
const flavor = flavorList.find((item) => item.name === value);
- return flavor
- ? renderFlavorSelected(flavor)
- : ((value ?? '') as React.ReactNode);
+ return flavor ? (
+
+ ) : (
+ ((value ?? '') as React.ReactNode)
+ );
}}
/>
diff --git a/src/pages/gpu-service/instance-types/hooks/use-instance-type-columns.tsx b/src/pages/gpu-service/instance-types/hooks/use-instance-type-columns.tsx
index cc1342f2..e3fe7c0f 100644
--- a/src/pages/gpu-service/instance-types/hooks/use-instance-type-columns.tsx
+++ b/src/pages/gpu-service/instance-types/hooks/use-instance-type-columns.tsx
@@ -3,22 +3,20 @@ import {
AutoTooltip,
DropdownButtons,
icons,
- StatusTag,
- ThemeTag
+ StatusTag
} from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
-import { Flex, Space, Tooltip } from 'antd';
+import { Space, Tooltip } from 'antd';
import type { ColumnsType } from 'antd/lib/table';
import _ from 'lodash';
import { useMemo } from 'react';
-import { formatMemoryDisplay } from '../../instances/config';
-import { manufactureColorMap } from '../../templates/config';
+import { ceilMilliToCore, parseQuantityToGi } from '../../utils';
+import { FlavorOption } from '../components/flavor-display';
import {
- ceilMilliToCore,
- formatManufacturer,
- parseQuantityToGi
-} from '../../utils';
-import { InstanceTypePhaseLabelMap, status as phaseStatusMap } from '../config';
+ InstanceTypePhaseLabelMap,
+ InstanceTypePhaseValueMap,
+ status as phaseStatusMap
+} from '../config';
import { ListItem } from '../config/types';
interface ColumnsHookProps {
@@ -26,16 +24,36 @@ interface ColumnsHookProps {
}
// DropdownButtons reads `locale` / `props` at runtime; its `items` prop is
-// typed as antd's MenuProps['items'], so cast the config to satisfy it.
-const rowActions = [
- {
+// typed as antd's MenuProps['items'], so cast the config to satisfy it. The
+// activate / deactivate action is chosen from the row's current phase: Active
+// types can be deactivated, Inactive ones activated (none while Preparing).
+const buildRowActions = (record: ListItem) => {
+ const phase = record.status?.phase;
+ const actions: any[] = [];
+ if (phase === InstanceTypePhaseValueMap.Active) {
+ actions.push({
+ label: 'gpuservice.instanceType.deactivate',
+ key: 'deactivate',
+ locale: true,
+ icon: icons.Disabled
+ });
+ } else if (phase === InstanceTypePhaseValueMap.Inactive) {
+ actions.push({
+ label: 'gpuservice.instanceType.activate',
+ key: 'activate',
+ locale: true,
+ icon: icons.Charger
+ });
+ }
+ actions.push({
label: 'common.button.delete',
key: 'delete',
locale: true,
icon: icons.DeleteOutlined,
props: { danger: true }
- }
-] as any;
+ });
+ return actions;
+};
// Column header with an info tooltip (used for the per-GPU resource columns).
const TitleWithTip: React.FC<{ title: string; tip: string }> = ({
@@ -64,75 +82,31 @@ const useInstanceTypeColumns = ({
dataIndex: 'name',
key: 'name',
ellipsis: { showTitle: false },
- render: (text: string) => (
-
- {text || '-'}
-
- )
+ // Prefer the friendly display name, fall back to the resource name.
+ render: (text: string, record: ListItem) => {
+ const label = record.spec?.displayName || text;
+ return (
+
+ {label || '-'}
+
+ );
+ }
},
{
- title: intl.formatMessage({ id: 'gpuservice.instanceType.product' }),
+ // Flavor cell mirrors the create drawer's dropdown: product name on
+ // top, manufacturer · memory · sliceable on the meta line below.
+ title: intl.formatMessage({ id: 'gpuservice.instanceType.flavor' }),
dataIndex: ['spec', 'product'],
key: 'product',
ellipsis: { showTitle: false },
- render: (text: string, record: ListItem) => (
-
-
- {text || '-'}
-
- {record.spec?.sliceable && (
-
- {intl.formatMessage({ id: 'gpuservice.instance.sliceable' })}
-
- )}
-
+ render: (_text: string, record: ListItem) => (
+
)
},
- {
- title: intl.formatMessage({ id: 'resources.table.vendor' }),
- dataIndex: ['spec', 'manufacturer'],
- key: 'manufacturer',
- ellipsis: { showTitle: false },
- render: (value: string) =>
- value ? (
-
- {formatManufacturer(value)}
-
- ) : (
- '-'
- )
- },
- {
- title: intl.formatMessage({ id: 'gpuservice.instance.memory' }),
- dataIndex: ['spec', 'memory'],
- key: 'memory',
- ellipsis: { showTitle: false },
- // Non-acceleratable (generic) types have no VRAM concept → N/A.
- render: (value: string, record: ListItem) =>
- record.spec?.acceleratable
- ? formatMemoryDisplay(value ?? undefined) || '-'
- : 'N/A'
- },
- // {
- // title: intl.formatMessage({ id: 'gpuservice.instance.sliceable' }),
- // dataIndex: ['spec', 'sliceable'],
- // key: 'sliceable',
- // ellipsis: { showTitle: false },
- // render: (value: boolean) =>
- // value
- // ? intl.formatMessage({ id: 'common.table.yes' })
- // : intl.formatMessage({ id: 'common.table.no' })
- // },
{
title: (
{
@@ -202,9 +176,9 @@ const useInstanceTypeColumns = ({
- {arch ? `${os} (${arch})` : os}
+ {arch ? `${os}/${arch}` : os}
);
}
@@ -234,7 +208,7 @@ const useInstanceTypeColumns = ({
ellipsis: { showTitle: false },
render: (_text, record: ListItem) => (
handleSelect(val, record)}
/>
)
diff --git a/src/pages/gpu-service/instance-types/index.tsx b/src/pages/gpu-service/instance-types/index.tsx
index 3330ff37..8c0ecba9 100644
--- a/src/pages/gpu-service/instance-types/index.tsx
+++ b/src/pages/gpu-service/instance-types/index.tsx
@@ -12,7 +12,11 @@ import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, Divider, Flex, Table, message } from 'antd';
import { useEffect, useMemo, useRef, useState } from 'react';
import PageBox, { HeaderLeft } from '../../_components/page-box';
-import { deleteGPUInstanceType } from './apis';
+import {
+ activateGPUInstanceType,
+ deactivateGPUInstanceType,
+ deleteGPUInstanceType
+} from './apis';
import AddInstanceTypeModal from './components/add-instance-type-modal';
import { FormData, ListItem } from './config/types';
import useCreateInstanceTypeModal from './hooks/use-create-instance-type-modal';
@@ -120,9 +124,29 @@ const GPUServiceInstanceTypes: React.FC = () => {
});
});
+ const handleToggleActive = useMemoizedFn(
+ async (record: ListItem, activate: boolean) => {
+ if (clusterId == null) return;
+ try {
+ const action = activate
+ ? activateGPUInstanceType
+ : deactivateGPUInstanceType;
+ await action({ name: record.name, cluster_id: clusterId });
+ message.success(intl.formatMessage({ id: 'common.message.success' }));
+ fetchInstanceTypes(clusterId);
+ } catch (error) {
+ // handled by the request interceptor
+ }
+ }
+ );
+
const handleSelect = useMemoizedFn((val: string, record: ListItem) => {
if (val === 'delete') {
handleDelete(record);
+ } else if (val === 'activate') {
+ handleToggleActive(record, true);
+ } else if (val === 'deactivate') {
+ handleToggleActive(record, false);
}
});
diff --git a/src/pages/gpu-service/instances/components/instance-type-item.tsx b/src/pages/gpu-service/instances/components/instance-type-item.tsx
index dd91b71d..23d8a471 100644
--- a/src/pages/gpu-service/instances/components/instance-type-item.tsx
+++ b/src/pages/gpu-service/instances/components/instance-type-item.tsx
@@ -115,7 +115,7 @@ function getInstanceDerived(item: InstanceTypeItemModel) {
acceleratable,
isGPU: acceleratable,
manufacturer: acceleratable ? spec.manufacturer || '' : 'cpu', // GPU manufacturer or 'cpu' for non-acceleratable types
- displayName: acceleratable ? spec.product || item.name : 'CPU Only',
+ displayName: acceleratable ? spec.product || item.name : 'CPU-only',
ramUnit: spec.unitResourcesParsed?.ram?.value,
os: _.capitalize(spec.os) || '',
arch: spec.arch,
diff --git a/src/pages/gpu-service/instances/utils/render-instance-type.tsx b/src/pages/gpu-service/instances/utils/render-instance-type.tsx
index 533d9b16..eb170ce8 100644
--- a/src/pages/gpu-service/instances/utils/render-instance-type.tsx
+++ b/src/pages/gpu-service/instances/utils/render-instance-type.tsx
@@ -131,7 +131,7 @@ export const renderInstanceType = (
// Types breakdown only wants CPU + RAM, for example.
categories?: SpecCategory[];
// Override the primary label (default: derived " x " /
- // "CPU Only"). The Instance Types breakdown keeps its plain product name.
+ // "CPU-only"). The Instance Types breakdown keeps its plain product name.
title?: string;
}
) => {
@@ -148,7 +148,7 @@ export const renderInstanceType = (
? isSliced
? `${description.product} (${sliceMemoryPercentage}%)`
: `${description.product} x ${accelerator}`
- : 'CPU Only');
+ : 'CPU-only');
const volume = (record.spec as any)?.volume;
// Spec popover grouped by category (GPU / CPU / Memory / Disk), mirroring
@@ -251,7 +251,7 @@ const mibToGiQuantity = (mib?: number): string | undefined =>
export interface InstanceTypeMiB {
name?: string;
product?: string;
- // accelerator (GPU card) count; 0/undefined → CPU-only ("CPU Only").
+ // accelerator (GPU card) count; 0/undefined → CPU-only.
gpuCount?: number;
// Per-card values.
unitCpuMilli?: number;
diff --git a/src/pages/usage/apis/resource.ts b/src/pages/usage/apis/resource.ts
index f65e6213..412c0143 100644
--- a/src/pages/usage/apis/resource.ts
+++ b/src/pages/usage/apis/resource.ts
@@ -93,7 +93,7 @@ export interface ResourceBreakdownItem extends ResourceBreakdownSummary {
unit_memory_mib?: number;
vram_mib?: number;
// Instance totals (requested cpu/ram) — the real size, so CPU instance types
- // show "CPU Only · 2 vCPU · 4 GB" instead of just the per-unit spec.
+ // show "CPU-only · 2 vCPU · 4 GB" instead of just the per-unit spec.
cpu_milli?: number;
memory_mib?: number;
// Per-instance rows also carry the card count + ephemeral disk so the
@@ -382,7 +382,7 @@ function flattenItem(
if (it.creator_deleted != null) flat.user_deleted = !!it.creator_deleted;
// Instance-type grouped trend: the series label (``group``) defaults to the
// raw flavor slug. Instance Types are grouped by actual shape, so label each
- // series by that shape — " x " / "CPU Only · 3 vCPU · 6 GB" —
+ // series by that shape — " x " / "CPU-only · 3 vCPU · 6 GB" —
// matching the table and keeping every shape a distinct series (#5700).
// ``groupBy`` is the unmapped frontend dimension; the instance-type axis is
// ``gpu_type`` (→ backend ``instance_type`` via GROUP_BY_MAP).
diff --git a/src/pages/usage/instances-tab/tables/use-instances-columns.tsx b/src/pages/usage/instances-tab/tables/use-instances-columns.tsx
index dc299db5..1b9a2be6 100644
--- a/src/pages/usage/instances-tab/tables/use-instances-columns.tsx
+++ b/src/pages/usage/instances-tab/tables/use-instances-columns.tsx
@@ -39,7 +39,7 @@ const useInstancesColumns = (groupKey: GroupKey) => {
}
];
// Instance Types breakdown: the pretty product name (or flavor slug for
- // older rows; "CPU Only" when no GPU cards) plus a CPU + RAM spec popover —
+ // older rows; "CPU-only" when no GPU cards) plus a CPU + RAM spec popover —
// rendered through the canonical renderer so the formatting matches the GPU
// Instances list, but limited to the CPU/RAM categories.
const instanceTypeColType = {
@@ -64,7 +64,7 @@ const useInstancesColumns = (groupKey: GroupKey) => {
intl,
categories: ['cpu', 'ram'],
// Each row is one shape: GPU " x ", CPU
- // "CPU Only · ".
+ // "CPU-only · ".
title: instanceTypeSeriesLabel(row)
}
);
@@ -95,7 +95,7 @@ const useInstancesColumns = (groupKey: GroupKey) => {
persistentMib: row.persistent_mib
}),
// Label by shape directly (consistent with the Instance Types
- // column); avoids renderInstanceType's "CPU Only" fallback when a
+ // column); avoids renderInstanceType's "CPU-only" fallback when a
// GPU row has vram but a missing/zero gpu_count.
{ intl, title: instanceTypeSeriesLabel(row) }
);
diff --git a/src/pages/usage/utils/format-instance-type.ts b/src/pages/usage/utils/format-instance-type.ts
index 6a55d99f..f0477ef3 100644
--- a/src/pages/usage/utils/format-instance-type.ts
+++ b/src/pages/usage/utils/format-instance-type.ts
@@ -32,16 +32,16 @@ export const formatCpuSpec = (
return parts.join(' · ');
};
-// CPU instance-type label: "CPU Only" plus its real size when known, e.g.
-// "CPU Only · 2 vCPU · 4 GB". Used by both the table column and the trend
+// CPU instance-type label: "CPU-only" plus its real size when known, e.g.
+// "CPU-only · 2 vCPU · 4 GB". Used by both the table column and the trend
// legend so they read identically.
export const cpuOnlyLabel = (row?: Partial): string => {
const spec = formatCpuSpec(row?.cpu_milli, row?.memory_mib);
- return spec ? `CPU Only · ${spec}` : 'CPU Only';
+ return spec ? `CPU-only · ${spec}` : 'CPU-only';
};
// Instance Types are grouped by actual shape, so each row is one concrete
-// type: a GPU shows " x ", a CPU shows "CPU Only · ".
+// type: a GPU shows " x ", a CPU shows "CPU-only · ".
// One label for the table column and the trend legend so they read the same
// and each shape is a distinct series. (" x " matches the GPU Instances list.)
export const instanceTypeSeriesLabel = (