refactor: gpu service API alignment
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export interface FormContextValue {
|
||||
action: PageActionType;
|
||||
currentData: any;
|
||||
}
|
||||
|
||||
export const FormContext = createContext<FormContextValue>({
|
||||
action: 'create',
|
||||
currentData: null
|
||||
});
|
||||
|
||||
export const FormContextProvider = FormContext.Provider;
|
||||
@@ -3,11 +3,6 @@ import { StatusType } from '@/config/types';
|
||||
import { IconFont, icons } from '@gpustack/core-ui';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import {
|
||||
InstanceTypeItem,
|
||||
InstanceTypeResource,
|
||||
InstanceTypeStatus
|
||||
} from './types';
|
||||
|
||||
export const InstanceStatusValueMap = {
|
||||
Scheduling: 'Scheduling',
|
||||
@@ -45,13 +40,22 @@ export const status: Record<string, StatusType> = {
|
||||
[InstanceStatusValueMap.Ready]: StatusMaps.success
|
||||
};
|
||||
|
||||
// Lifecycle actions (logs/events/start/stop) require K8s proxy endpoints that
|
||||
// the new /v2/gpu-instances API does not yet expose; keep them visible but
|
||||
// disabled until backend support lands.
|
||||
export const rowActionList = [
|
||||
{
|
||||
label: 'common.button.view',
|
||||
key: 'view',
|
||||
label: 'common.button.edit',
|
||||
key: 'edit',
|
||||
locale: true,
|
||||
icon: icons.DetailInfo
|
||||
icon: icons.EditOutlined
|
||||
},
|
||||
// {
|
||||
// label: 'common.button.view',
|
||||
// key: 'view',
|
||||
// locale: true,
|
||||
// icon: icons.DetailInfo
|
||||
// },
|
||||
{
|
||||
label: 'common.button.viewlog',
|
||||
key: 'viewlog',
|
||||
@@ -64,30 +68,30 @@ export const rowActionList = [
|
||||
locale: true,
|
||||
icon: icons.ProfileOutlined
|
||||
},
|
||||
{
|
||||
label: 'common.button.start',
|
||||
key: 'start',
|
||||
locale: true,
|
||||
icon: icons.Play
|
||||
},
|
||||
{
|
||||
label: 'common.button.stop',
|
||||
key: 'stop',
|
||||
locale: true,
|
||||
icon: icons.Stop
|
||||
},
|
||||
// {
|
||||
// label: 'common.button.edit',
|
||||
// key: 'edit',
|
||||
// label: 'common.button.start',
|
||||
// key: 'start',
|
||||
// locale: true,
|
||||
// icon: icons.EditOutlined
|
||||
// icon: icons.Play,
|
||||
// props: {
|
||||
// disabled: true
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// label: 'common.button.stop',
|
||||
// key: 'stop',
|
||||
// locale: true,
|
||||
// icon: icons.Stop,
|
||||
// props: {
|
||||
// disabled: true
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// label: 'common.button.recreate',
|
||||
// key: 'recreate',
|
||||
// locale: true,
|
||||
// icon: icons.RetweetOutlined
|
||||
// },
|
||||
{
|
||||
label: 'common.button.recreate',
|
||||
key: 'recreate',
|
||||
locale: true,
|
||||
icon: icons.RetweetOutlined
|
||||
},
|
||||
{
|
||||
label: 'common.button.delete',
|
||||
key: 'delete',
|
||||
@@ -103,12 +107,18 @@ export const batchActionList = [
|
||||
{
|
||||
label: 'common.button.start',
|
||||
key: 'start',
|
||||
icon: icons.Play
|
||||
icon: icons.Play,
|
||||
props: {
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'common.button.stop',
|
||||
key: 'stop',
|
||||
icon: icons.Stop
|
||||
icon: icons.Stop,
|
||||
props: {
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'common.button.delete',
|
||||
@@ -121,8 +131,6 @@ export const batchActionList = [
|
||||
];
|
||||
|
||||
export const InstanceTypePhaseValueMap = {
|
||||
// Available: 'Available',
|
||||
// Unavailable: 'Unavailable'
|
||||
PreParing: 'Preparing',
|
||||
Inactive: 'Inactive',
|
||||
Active: 'Active'
|
||||
@@ -141,10 +149,12 @@ export const InstanceTypePhaseStatus: Record<string, StatusType> = {
|
||||
};
|
||||
|
||||
export const StorageModeValueMap = {
|
||||
Existing: 'existing',
|
||||
Temporary: 'temporary'
|
||||
Temporary: 'temporary',
|
||||
Persistent: 'persistent'
|
||||
};
|
||||
|
||||
export const DEFAULT_PV_CAPACITY_GB = 20;
|
||||
|
||||
// Constant SSH public key resource name used when SSH is enabled
|
||||
export const DEFAULT_SSH_PUBLIC_KEY_NAME = 'default';
|
||||
|
||||
@@ -163,49 +173,40 @@ export const convertKiToGi = (value?: string): string | undefined => {
|
||||
return `${_.round(Number(num) / GI_DIVISOR[unit], 2)}Gi`;
|
||||
};
|
||||
|
||||
export const transformInstanceTypeResource = (
|
||||
resource?: InstanceTypeResource
|
||||
): InstanceTypeResource | undefined => {
|
||||
if (!resource) return resource;
|
||||
return {
|
||||
...resource,
|
||||
capacity: convertKiToGi(
|
||||
resource.capacity
|
||||
) as InstanceTypeResource['capacity'],
|
||||
onceMaxRequest: convertKiToGi(
|
||||
resource.onceMaxRequest
|
||||
) as InstanceTypeResource['onceMaxRequest'],
|
||||
remaining: convertKiToGi(
|
||||
resource.remaining
|
||||
) as InstanceTypeResource['remaining']
|
||||
};
|
||||
const parseQuantity = (value?: string | null): number => {
|
||||
if (!value) return 0;
|
||||
const match = /^(-?\d+(?:\.\d+)?)/.exec(String(value));
|
||||
return match ? Number(match[1]) || 0 : 0;
|
||||
};
|
||||
|
||||
export const transformInstanceType = (
|
||||
item: InstanceTypeItem
|
||||
): InstanceTypeItem => {
|
||||
if (!item?.status) return item;
|
||||
const status = item.status;
|
||||
return {
|
||||
...item,
|
||||
spec: {
|
||||
...item.spec,
|
||||
manufacturer: !item.spec?.acceleratable ? 'cpu' : item.spec.manufacturer
|
||||
},
|
||||
status: {
|
||||
...status,
|
||||
accelerator: transformInstanceTypeResource(
|
||||
status.accelerator
|
||||
) as InstanceTypeStatus['accelerator'],
|
||||
cpu: transformInstanceTypeResource(
|
||||
status.cpu
|
||||
) as InstanceTypeStatus['cpu'],
|
||||
localStorage: transformInstanceTypeResource(
|
||||
status.localStorage
|
||||
) as InstanceTypeStatus['localStorage'],
|
||||
ram: transformInstanceTypeResource(
|
||||
status.ram
|
||||
) as InstanceTypeStatus['ram']
|
||||
}
|
||||
};
|
||||
// Returns the slider max for the accelerator count: the largest
|
||||
// tier.onceMaxRequest across all acceleratorTiers (not from candidates).
|
||||
export const getAcceleratorMax = (
|
||||
tiers?: { onceMaxRequest: string }[] | null
|
||||
) => {
|
||||
if (!tiers?.length) return 0;
|
||||
return tiers.reduce((acc, tier) => {
|
||||
const n = parseQuantity(tier.onceMaxRequest);
|
||||
return n > acc ? n : acc;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
// Picks the candidate (cluster + type name) that should fulfill a requested
|
||||
// accelerator count: the first candidate of the smallest tier whose
|
||||
// onceMaxRequest is >= the requested count.
|
||||
export const pickCandidateForAccelerator = <
|
||||
C extends { cluster: string; name: string }
|
||||
>(
|
||||
tiers:
|
||||
| { onceMaxRequest: string; candidates?: C[] | null }[]
|
||||
| undefined
|
||||
| null,
|
||||
count: number
|
||||
): C | null => {
|
||||
if (!tiers?.length) return null;
|
||||
const sorted = [...tiers].sort(
|
||||
(a, b) => parseQuantity(a.onceMaxRequest) - parseQuantity(b.onceMaxRequest)
|
||||
);
|
||||
const tier = sorted.find((t) => parseQuantity(t.onceMaxRequest) >= count);
|
||||
return tier?.candidates?.[0] ?? null;
|
||||
};
|
||||
|
||||
@@ -2,16 +2,16 @@ export type ImagePullPolicy = 'Always' | 'IfNotPresent' | 'Never';
|
||||
|
||||
// instance form data
|
||||
export interface FormData {
|
||||
metadata: {
|
||||
name: string;
|
||||
namespace: string;
|
||||
};
|
||||
name: string;
|
||||
clusterId?: number | null;
|
||||
owner_principal_id?: number | null;
|
||||
displayName?: string | null;
|
||||
description?: string | null;
|
||||
enable_ssh?: boolean;
|
||||
spec: {
|
||||
type: string;
|
||||
image: string;
|
||||
imagePullPolicy: ImagePullPolicy;
|
||||
displayName: string;
|
||||
command: string[];
|
||||
ports: {
|
||||
port: number;
|
||||
@@ -27,9 +27,11 @@ export interface FormData {
|
||||
cpu?: string;
|
||||
ram?: string;
|
||||
localStorage?: string;
|
||||
accelerator?: string;
|
||||
// Stored in the form as a number so NumberSelection's strict
|
||||
// equality picks up the active item. Serialized to a string at the
|
||||
// API boundary in handleFinish.
|
||||
accelerator?: number | string;
|
||||
};
|
||||
description: string;
|
||||
volume: {
|
||||
ephemeral?: {
|
||||
capacity?: string;
|
||||
@@ -37,162 +39,136 @@ export interface FormData {
|
||||
persistent?: {
|
||||
name: string;
|
||||
};
|
||||
persistentTemplate?: {
|
||||
name?: string;
|
||||
spec: {
|
||||
type: string;
|
||||
capacity: string;
|
||||
};
|
||||
releaseWithInstance?: boolean;
|
||||
};
|
||||
};
|
||||
sshPublicKey: {
|
||||
name: string;
|
||||
};
|
||||
sshPublicKeys?: { name: string }[];
|
||||
};
|
||||
}
|
||||
|
||||
export type InstanceServicePortProtocol = 'TCP' | 'UDP';
|
||||
|
||||
export interface InstanceIP {
|
||||
ip: string;
|
||||
}
|
||||
|
||||
export interface InstanceServicePort {
|
||||
port: number;
|
||||
name: string;
|
||||
nodePort?: number;
|
||||
protocol?: InstanceServicePortProtocol;
|
||||
}
|
||||
|
||||
export interface InstanceStatus {
|
||||
hostIPs?: {
|
||||
ip: string;
|
||||
}[];
|
||||
namespace?: string;
|
||||
phase?: string;
|
||||
phaseMessage?: string;
|
||||
podIPs?: {
|
||||
ip: string;
|
||||
}[];
|
||||
ports?: {
|
||||
port: number;
|
||||
name: string;
|
||||
nodePort?: number;
|
||||
protocol?: InstanceServicePortProtocol;
|
||||
}[];
|
||||
hostIPs?: InstanceIP[];
|
||||
podIPs?: InstanceIP[];
|
||||
ports?: InstanceServicePort[];
|
||||
}
|
||||
|
||||
// instance list item
|
||||
export interface ListItem extends Omit<FormData, 'metadata'> {
|
||||
export interface ListItem extends FormData {
|
||||
id: number;
|
||||
status?: InstanceStatus;
|
||||
metadata: {
|
||||
name: string;
|
||||
namespace?: string;
|
||||
uid: string;
|
||||
resourceVersion: string;
|
||||
creationTimestamp: string;
|
||||
annotations?: Record<string, string>;
|
||||
managedFields?: {
|
||||
manager: string;
|
||||
operation: string;
|
||||
apiVersion: string;
|
||||
time: string;
|
||||
fieldsType: string;
|
||||
fieldsV1: Record<string, any>;
|
||||
}[];
|
||||
};
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at?: string | null;
|
||||
status?: InstanceStatus | null;
|
||||
}
|
||||
|
||||
type Quality =
|
||||
| `${number}`
|
||||
| `${number}Ki`
|
||||
| `${number}Mi`
|
||||
| `${number}Gi`
|
||||
| `${number}Ti`;
|
||||
// =========== Instance Types ===========
|
||||
|
||||
export interface InstanceTypeResource {
|
||||
capacity?: Quality;
|
||||
onceMaxRequest?: Quality;
|
||||
remaining?: Quality;
|
||||
onceMaxRequest: string;
|
||||
remaining: string;
|
||||
capacity: string;
|
||||
}
|
||||
|
||||
export interface InstanceTypeStatus {
|
||||
export interface InstanceTypeCandidate {
|
||||
cluster: string;
|
||||
name: string;
|
||||
accelerator: InstanceTypeResource;
|
||||
cpu: InstanceTypeResource;
|
||||
localStorage: InstanceTypeResource;
|
||||
phase: string;
|
||||
phaseMessage?: string;
|
||||
ram: InstanceTypeResource;
|
||||
localStorage: InstanceTypeResource;
|
||||
}
|
||||
|
||||
export interface InstanceTypeMetadata {
|
||||
name: string;
|
||||
namespace?: string;
|
||||
uid?: string;
|
||||
resourceVersion?: string;
|
||||
creationTimestamp?: string;
|
||||
generateName?: string;
|
||||
generation?: number;
|
||||
finalizers?: string[];
|
||||
export interface InstanceTypeTier {
|
||||
onceMaxRequest: string;
|
||||
candidates?: InstanceTypeCandidate[] | null;
|
||||
}
|
||||
|
||||
export interface InstanceTypeRemainingResource {
|
||||
accelerator?: string | null;
|
||||
cpu: string;
|
||||
ram: string;
|
||||
localStorage: string;
|
||||
}
|
||||
|
||||
export interface InstanceTypeSpec {
|
||||
acceleratable: boolean;
|
||||
group: string;
|
||||
computeCapability?: string;
|
||||
family?: string;
|
||||
manufacturer?: string;
|
||||
memory?: string;
|
||||
product?: string;
|
||||
sliced?: number;
|
||||
acceleratable: boolean;
|
||||
manufacturer: string;
|
||||
product?: string | null;
|
||||
memory?: string | null;
|
||||
family?: string | null;
|
||||
computeCapability?: string | null;
|
||||
sliced?: string | null;
|
||||
}
|
||||
|
||||
export interface InstanceTypeStatus {
|
||||
remaining: InstanceTypeRemainingResource;
|
||||
acceleratorTiers?: InstanceTypeTier[] | null;
|
||||
}
|
||||
|
||||
export interface InstanceTypeItem {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
id: number;
|
||||
metadata: InstanceTypeMetadata;
|
||||
name: string;
|
||||
spec: InstanceTypeSpec;
|
||||
status: InstanceTypeStatus;
|
||||
}
|
||||
|
||||
export interface InstanceEventObjectReference {
|
||||
apiVersion?: string;
|
||||
fieldPath?: string;
|
||||
kind?: string;
|
||||
name?: string;
|
||||
namespace?: string;
|
||||
resourceVersion?: string;
|
||||
uid?: string;
|
||||
}
|
||||
|
||||
export interface InstanceEventSource {
|
||||
component?: string;
|
||||
host?: string;
|
||||
}
|
||||
|
||||
export interface InstanceEventSeries {
|
||||
count?: number;
|
||||
lastObservedTime?: string;
|
||||
}
|
||||
// =========== Events / logs (placeholder) ===========
|
||||
// The /v2/gpu-instances API does not yet expose log/event endpoints. These
|
||||
// shapes are retained as minimal placeholders so disabled UI continues to
|
||||
// compile until backend support lands.
|
||||
|
||||
export interface InstanceEventItem {
|
||||
action?: string;
|
||||
apiVersion?: string;
|
||||
type?: string;
|
||||
reason?: string;
|
||||
message?: string;
|
||||
count?: number;
|
||||
eventTime?: string;
|
||||
firstTimestamp?: string;
|
||||
involvedObject: InstanceEventObjectReference;
|
||||
kind?: string;
|
||||
lastTimestamp?: string;
|
||||
message?: string;
|
||||
metadata: {
|
||||
name?: string;
|
||||
namespace?: string;
|
||||
uid?: string;
|
||||
resourceVersion?: string;
|
||||
creationTimestamp?: string;
|
||||
};
|
||||
reason?: string;
|
||||
related?: InstanceEventObjectReference;
|
||||
reportingComponent?: string;
|
||||
reportingInstance?: string;
|
||||
series?: InstanceEventSeries;
|
||||
source?: InstanceEventSource;
|
||||
type?: string;
|
||||
source?: {
|
||||
component?: string;
|
||||
host?: string;
|
||||
};
|
||||
series?: {
|
||||
count?: number;
|
||||
lastObservedTime?: string;
|
||||
};
|
||||
metadata?: {
|
||||
name?: string;
|
||||
namespace?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface InstanceEvents {
|
||||
apiVersion?: string;
|
||||
kind?: string;
|
||||
metadata?: {
|
||||
resourceVersion?: string;
|
||||
};
|
||||
items: InstanceEventItem[];
|
||||
}
|
||||
|
||||
export type InstanceLog = string;
|
||||
|
||||
export interface InstanceLogQueryParams {
|
||||
follow?: boolean;
|
||||
limitBytes?: number;
|
||||
@@ -201,5 +177,3 @@ export interface InstanceLogQueryParams {
|
||||
timestamps?: boolean;
|
||||
pretty?: string;
|
||||
}
|
||||
|
||||
export type InstanceLog = string;
|
||||
|
||||
Reference in New Issue
Block a user