Chuyển tới nội dung chính

GPU Inventory

Part of the Scheduler feature area (axap-gpu-volcano-ui, MFE id 'scheduler'). Sibling docs: gpu-overview, workload, timetable.

Overview

GPU Inventory (schedulerGpuInventory) is a cluster > node > GPU chip inventory screen with Explorer / Topology / List views, a KPI row, filters, and a GPU detail drawer. It lives at modules/volcano/dashboard/pages/GpuInventory/index.vue with siblings GpuExplorer.vue, NodeTile.vue, GpuDetailDrawer.vue, inventoryNav.ts.

Its primary real-data source is zcp-mcm-go (/mcm/resource/v1beta1/gpu/instances), a DCGM-telemetry service — unlike GPU Overview/Time Table, this page does not go through the Volcano scheduler backend directly for its GPU list.

A demo mode is baked into this codebase and is ON by default (modules/volcano/dashboard/demo/isDemo.ts) — disabled only per-tab via ?demo=0. In demo mode this page uses DEMO_FULL_NODES/DEMO_MIG_NODES/DEMO_TS_NODES/DEMO_TOPO_SUMMARY from src/modules/volcano/dashboard/demo/demoData.ts instead of calling the backend.

Route Design

Route constants: constants/routeNames/index.ts, paths: constants/routes/index.ts, lazy components: pages/index.ts. Registered in both the system and project route tables built by buildRoutes() in router/index.ts.

Route nameRaw pathSystem pathProject pathComponent
ROUTE_NAMES.GPU_INVENTORY (schedulerGpuInventory)/gpu-inventory/system/scheduler/gpu-inventory/proj/:projectId/scheduler/gpu-inventoryGpuInventory/index.vue — router/index.ts

No hidden/drill-down routes belong to this screen; the GPU detail view is an in-page GpuDetailDrawer.vue (not a route), so selectedGpu state alone controls visibility.

Route mechanics common to the whole Scheduler MFE (system vs. project route table selection, getHostProjectId() scope-locking) are the same as documented in gpu-overview and apply here too — this page reads getHostProjectId()-equivalent scope info indirectly via resolveCluster() (useProfileStore), not from its own route meta.

UI Structure

GPU Inventory → GpuInventory/index.vue
├── Page head: title + description + 3-way view toggle (익스플로러 / 토폴로지 / 목록)
├── KPI row: 노드 / 총 GPU / 가동 / 유휴 점유 / 미할당 / 가동률 (+ bar)
├── Filter bar: 클러스터 / GPU 모델 / GPU 상태 / 타입(FULL/MIG/TS) selects + 검색 + active-filter tags
├── Loading / Error states
├── Explorer view → GpuExplorer.vue (cluster>node>GPU tree + tabbed detail, resizable tree pane)
├── Topology view → legend card + NodeTile.vue grid (per-node GPU cell grid)
├── List view → inline <table> of all GPUs (node/model/type/uuid/status/util/pool/workload)
└── GpuDetailDrawer.vue (side drawer, opened from any of the 3 views via openDrawer/cell click)

Component Design

  • GpuInventory/index.vue: owns allGpus (flat list of NormalizedGpu), filters (reactive: cluster/model/status/type/text), viewMode ('explorer'|'topology'|'list'), loading/error, selectedGpu. KPI values (summary.nodes/total/allocated/idle/free/ allocPct) and filtered lists (filteredGpus, filteredNodes) are computed from allGpus + filters (computed definitions not fully traced in this pass — see Assumptions).
  • GpuExplorer.vue: left resizable tree (treeWidth, flatRows, treeQuery filter, toggle()/select()) showing Cluster → Node → GPU rows with expand/collapse chevrons; selecting a row drives a right-hand tabbed detail panel (tabs not enumerated in this pass).
  • NodeTile.vue: renders one node’s GPUs as a cell grid (cells prop = node’s GPU list); emits cell-click up to the page’s openDrawer().
  • GpuDetailDrawer.vue: side drawer bound to :gpu="selectedGpu", closes via @close="selectedGpu = null".
  • inventoryNav.ts (takePendingInventoryNav()): a one-shot, in-memory (non-URL) hand-off channel — other pages (e.g. GPU Overview’s topology canvas “open inventory” action) can push a {gpu, tab, node, status, view} context that this page consumes once on mount, falling back to route.query (gpu/tab/node/status) if no pending nav context exists. On mount: if initialGpu is set, viewMode is forced to 'explorer' (and filters.text seeded with the node name); if initialStatus is set, filters.status is seeded and viewMode is set from navCtx.view (defaults 'list') — this is how a KPI count click elsewhere deep-links into a pre-filtered inventory list.

API Design

All calls use services.axiosInstance from @cloudz-mp/zmp-base-ui.

Primary data source — modules/volcano/dashboard/apis/mcmPodMetrics.ts

FunctionPathNotes
fetchGpuInstances(cluster?)GET {mcmBase}/gpu/instances?cluster&page=1&size=500&sort=index,instanceIdSole real-data call made by this page’s reload(); mcmBase = getMcmBasePath() = "/mcm/resource/v1beta1"
matchInstanceByPod(instances, namespace, podName)client-side helper, no HTTPmatches an instance to a workload pod (used elsewhere, e.g. PodVgpuDrawer)

resolveCluster() (from modules/volcano/dashboard/apis/index.ts) is imported directly into this page (import { resolveCluster } from "../../apis") to resolve the cluster passed to fetchGpuInstances.

GpuInstanceView response fields consumed: gpuUtilization, memoryUsed/memoryTotal, temperature, powerUsage, isMigInstance/isMigMode/isTimeSlicingMode, allocatedPods[] (name/namespace/project) — mapped by a page-local normalizeInstance() into the NormalizedGpu shape (util, memTotalGb/memUsedGb, tempC, powerW, pool, workload, workloads[], type).

No mutation endpoints (create/update/delete) are called from this screen — it is read-only.

Dev-time proxy: /api/mcm and /mcm/resource → zcp-mcm-go (localhost:8080) — vite.config.ts.

State Management

  • No Pinia store is used by this page directly — all inventory data (allGpus, filters, viewMode, loading, error, selectedGpu) is page-local ref/reactive state in GpuInventory/index.vue.
  • useProfileStore is used only indirectly, through resolveCluster(), to pick the active cluster for the fetchGpuInstances call — no direct read/write of profile state from this page’s own script beyond that import chain.
  • inventoryNav.ts acts as an ad hoc, module-scoped (non-reactive, one-shot) state channel for cross-page deep-linking — not a Pinia store, and not persisted.

Business Logic

  1. GPU status classification — GPU cells/rows are classified into running/idle/free/error states for filtering and KPI aggregation (filters.status options: running, idle(유휴 점유), free(미할당), error(오류)); exact derivation logic (e.g. thresholds distinguishing “idle” allocated-but-unused GPUs from “free” unallocated ones) was not traced to a specific function in this pass — see Assumptions.
  2. Type classification — type: "MIG" | "TS" | "FULL" is derived per-instance from inst.isMigInstance || inst.isMigMode → MIG, inst.isTimeSlicingMode → TS, else FULL (normalizeInstance()).
  3. Multi-occupancy exposure for shared GPUs — MIG/Time-Slicing GPUs can have multiple allocatedPods; the FE exposes all of them via a workloads[] array on each NormalizedGpu, not just the first occupant (comment: “공유 GPU(MIG/TS)는 allocatedPods 가 다수 — 전부 노출”).
  4. Deep-link view forcing — arriving via inventoryNav/route.query with a gpu value forces viewMode = 'explorer'; arriving with a status value forces filters.status and switches to the nav-specified view (default 'list') — see Component Design.
  5. Demo mode is default-ON — same cross-cutting rule as the other three screens; isDemoMode() is checked at the top of reload(), short-circuiting the real fetchGpuInstances call entirely.

Data Flow

flowchart TD
R[/system/scheduler/gpu-inventory/] --> P[GpuInventory/index.vue]
P -->|onMounted -> reload| DEMO{isDemoMode?}
DEMO -->|yes| D1[normalizeDemoData -> allGpus]
DEMO -->|no| A1[apis/mcmPodMetrics: fetchGpuInstances]
A1 --> BE[(zcp-mcm-go /mcm/resource/v1beta1)]
BE --> A1
A1 -->|instances.map normalizeInstance| P
P --> V1[Explorer view]
P --> V2[Topology view]
P --> V3[List view]
V1 & V2 & V3 -->|click| Drawer[GpuDetailDrawer.vue]

reload() is the single source of truth for this page’s GPU list; there is no polling timer in this page (unlike GPU Overview/Time Table) — data is fetched once on mount and on explicit “다시 시도” retry after an error.

Dependency Graph

flowchart LR
GpuInventory --> GpuExplorer
GpuInventory --> NodeTile
GpuInventory --> GpuDetailDrawer
GpuInventory --> inventoryNav["inventoryNav.ts"]
GpuInventory --> apisMcm["apis/mcmPodMetrics.ts (mcm)"]
GpuInventory --> apisIndex["apis/index.ts (resolveCluster)"]
apisMcm --> apisIndex
apisIndex --> useProfileStore

Sequence Flow

GPU Inventory load

sequenceDiagram
actor User
participant Page as GpuInventory/index.vue
participant API as apis/mcmPodMetrics.ts
participant BE as zcp-mcm-go

User->>Page: navigate to /system/scheduler/gpu-inventory
Page->>Page: onMounted -> reload()
alt demo mode
Page->>Page: allGpus = normalizeDemoData()
else real backend
Page->>API: fetchGpuInstances(resolveCluster())
API->>BE: GET /mcm/resource/v1beta1/gpu/instances?cluster&page&size&sort
BE-->>API: GpuInstanceView[]
API-->>Page: instances
Page->>Page: allGpus = instances.map(normalizeInstance)
end
Page-->>User: renders KPI row + Explorer/Topology/List view

GPU detail drawer open (User Action → UI Update)

sequenceDiagram
actor User
participant View as GpuExplorer.vue / NodeTile.vue / List <table>
participant Page as GpuInventory/index.vue
participant Drawer as GpuDetailDrawer.vue

User->>View: click a GPU row/tile/tree-node
View->>Page: emit cell-click(gpu) / select(row)
Page->>Page: openDrawer(gpu) -> selectedGpu = gpu
Page->>Drawer: :gpu="selectedGpu"
Drawer-->>User: renders GPU detail panel
User->>Drawer: click close
Drawer->>Page: emit close
Page->>Page: selectedGpu = null

Key Findings

  • This is the only one of the four Scheduler screens whose primary data source is zcp-mcm-go rather than the Volcano scheduler backend (/scheduler/v1) — it reads DCGM-telemetry-enriched instance data directly, and does not call fetchVolcanoQueues/fetchWorkloads at all.
  • No polling — unlike GPU Overview (configurable interval) and Time Table (fixed 30s), this page fetches once on mount; staleness requires a manual “다시 시도”/navigation refresh.
  • inventoryNav.ts is a deliberate non-URL deep-link mechanism — it avoids round-tripping navigation context through query params, but this also means a hard-refresh loses that context (falls back to route.query only if present).

Risks / Technical Debt

  • KPI/status derivation logic (idle vs. free vs. error thresholds) was not located in this pass — if this logic diverges from GPU Overview’s stuck/idle definitions, the two screens could report inconsistent “idle” counts for the same cluster. Flagged as Not Found in Code below pending a full read of the KPI summary computed.
  • No error normalizer — error.value = e instanceof Error ? e.message : "알 수 없는 오류" surfaces raw messages; no per-status (403/404/5xx) handling like GPU Pool CRUD has.
  • Single-cluster fetch only — fetchGpuInstances(cluster?) takes one cluster (default resolveCluster()), so this page cannot show an “All clusters” combined inventory the way Time Table’s “All” fan-out does.

Assumptions

  • The exact computation of summary (노드/총 GPU/가동/유휴 점유/미할당/가동률) and filteredGpus/filteredNodes computed properties was Not Found in Code in this pass (template bindings were observed, but the computed definitions themselves were not read) — treat the precise idle/free/error boundary rules as unconfirmed.
  • GpuExplorer.vue’s tabbed detail panel contents (what each tab shows) were Not Found in Code in this pass — only the tree/selection mechanics were inspected.
  • Backend service zcp-mcm-go’s actual endpoint implementation is external to this repo and was Not Found in Code — inferred only from the FE request shape and the GpuInstanceView TypeScript interface declared in this repo.