Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/userguide/interactive-manifest-generator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: Interactive Manifest Generator
sidebar_label: Manifest Generator
---

# Interactive Manifest Generator

Project HAMi supports GPU virtualization across a variety of hardware manufacturers, including NVIDIA, Cambricon, Hygon, Iluvatar, and Huawei. Each device requires specific Kubernetes resource keys in your container `resources.limits` to correctly allocate device memory and cores, and specific `metadata.annotations` to constrain to device types or UUIDs.

Use the interactive tool below to generate the exact YAML configuration needed for your use case. You can integrate the generated configuration directly into your deployment specifications (such as adding the resources to your `spec.template.spec.containers` section).

import ManifestGenerator from '@site/src/components/ManifestGenerator';

<ManifestGenerator />

## Advanced Options

- **Specific Device Type**: If you have a heterogeneous cluster (e.g. A100s and V100s), you can specify which device model your pod should be scheduled on.
- **Specific Device UUID**: If you need to bind a pod to a specific physical device for performance profiling or debugging, you can provide its UUID.

> **Note**: Not all vendors support core percentage or memory percentage scaling. The generator automatically adapts its options based on the selected device vendor's supported capabilities.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: 交互式 Manifest 生成器
sidebar_label: Manifest 生成器
---

# 交互式 Manifest 生成器

Project HAMi 支持跨多个硬件制造商的 GPU 虚拟化,包括 NVIDIA、寒武纪 (Cambricon)、海光 (Hygon)、天数智芯 (Iluvatar) 和华为升腾 (Huawei Ascend)。每个设备需要在您的容器 `resources.limits` 中使用特定的 Kubernetes 资源键以便正确分配设备内存和核心,并使用特定的 `metadata.annotations` 来限制设备类型或 UUID。

使用下方的交互式工具,为您的用例生成准确的 YAML 配置。您可以将生成的配置直接集成到您的部署规范中(例如将资源添加到您的 `spec.template.spec.containers` 部分)。

import ManifestGenerator from '@site/src/components/ManifestGenerator';

<ManifestGenerator />

## 高级选项 (Advanced Options)

- **特定设备类型 (Specific Device Type)**: 如果您有一个异构集群(例如 A100 和 V100 混合),您可以指定您的 Pod 应该调度到哪种设备型号上。
- **特定设备 UUID (Specific Device UUID)**: 如果您出于性能分析或调试目的,需要将 Pod 绑定到特定的物理设备,您可以提供其 UUID。

> **注意**: 并非所有供应商都支持核心百分比或内存百分比分配。生成器会根据所选设备供应商支持的功能,自动调整其选项。
1 change: 1 addition & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ module.exports = {
items: [
"userguide/configure",
"userguide/device-supported",
"userguide/interactive-manifest-generator",
"userguide/benchmark",
"userguide/hami-webui-user-guide",
{
Expand Down
264 changes: 264 additions & 0 deletions src/components/ManifestGenerator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
import React, { useState, useMemo } from 'react';
import CodeBlock from '@theme/CodeBlock';
import Translate, { translate } from '@docusaurus/Translate';
import clsx from 'clsx';
import styles from './styles.module.css';

const VENDORS = {
NVIDIA: {
name: 'NVIDIA (GPU)',
resourceKey: 'nvidia.com/gpu',
memKey: 'nvidia.com/gpumem',
memPctKey: 'nvidia.com/gpumem-percentage',
coreKey: 'nvidia.com/gpucores',
corePctKey: 'nvidia.com/gpucores-percentage',
typeKey: 'hami.io/vgpu-type',
uuidKey: 'hami.io/vgpu-uuid',
memUnit: 'MiB',
},
CAMBRICON: {
name: 'Cambricon (MLU)',
resourceKey: 'cambricon.com/vmlu',
memKey: 'cambricon.com/mlu.smlu.vmemory',
memPctKey: 'cambricon.com/mlu.smlu.vmemory',
coreKey: 'cambricon.com/mlu.smlu.smlu',
corePctKey: 'cambricon.com/mlu.smlu.smlu',
typeKey: 'hami.io/mlu-type',
uuidKey: 'hami.io/mlu-uuid',
memUnit: '%',
},
HYGON: {
name: 'Hygon (DCU)',
resourceKey: 'hygon.com/dcunum',
memKey: 'hygon.com/dcumem',
memPctKey: null,
coreKey: 'hygon.com/dcucores',
corePctKey: null,
typeKey: 'hami.io/dcu-type',
uuidKey: 'hami.io/dcu-uuid',
memUnit: 'MiB',
},
ILUVATAR: {
name: 'Iluvatar (GPU)',
resourceKey: 'iluvatar.ai/vgpu',
memKey: 'iluvatar.ai/vcuda-memory',
memPctKey: null,
coreKey: 'iluvatar.ai/vcuda-core',
corePctKey: null,
typeKey: 'hami.io/iluvatar-type',
uuidKey: 'hami.io/iluvatar-uuid',
memUnit: 'MiB',
},
ASCEND: {
name: 'Huawei Ascend (NPU)',
resourceKey: 'huawei.com/Ascend910',
memKey: 'huawei.com/Ascend910-memory',
memPctKey: null,
coreKey: null,
corePctKey: null,
typeKey: null,
uuidKey: null,
memUnit: 'MiB',
},
};

export default function ManifestGenerator() {
const [vendor, setVendor] = useState('NVIDIA');
const [deviceCount, setDeviceCount] = useState(1);
const [memMode, setMemMode] = useState('value');
const [memValue, setMemValue] = useState(3000);
const [coreMode, setCoreMode] = useState('none');
const [coreValue, setCoreValue] = useState(50);

const [advanced, setAdvanced] = useState(false);
const [deviceType, setDeviceType] = useState('');
const [deviceUuid, setDeviceUuid] = useState('');

// Compute yamlCode synchronously for bulletproof SSR
const yamlCode = useMemo(() => {
const v = VENDORS[vendor];
let annotations = [];
let limits = [];

limits.push(` ${v.resourceKey}: ${deviceCount}`);

if (v.memKey) {
if (memMode === 'value' && v.memUnit !== '%') {
limits.push(` ${v.memKey}: ${memValue}`);
} else if (memMode === 'percentage' && v.memPctKey) {
limits.push(` ${v.memPctKey}: ${memValue}`);
} else if (v.memUnit === '%') {
// Fallback if forced percentage logic
limits.push(` ${v.memPctKey || v.memKey}: ${memValue}`);
}
}

if (v.coreKey && coreMode !== 'none') {
if (coreMode === 'value') {
limits.push(` ${v.coreKey}: ${coreValue}`);
} else if (coreMode === 'percentage' && v.corePctKey) {
limits.push(` ${v.corePctKey}: ${coreValue}`);
}
}

if (advanced) {
if (deviceType && v.typeKey) {
annotations.push(` ${v.typeKey}: ${JSON.stringify(deviceType)}`);
}
if (deviceUuid && v.uuidKey) {
annotations.push(` ${v.uuidKey}: ${JSON.stringify(deviceUuid)}`);
}
}

let code = `apiVersion: v1
kind: Pod
metadata:
name: hami-${vendor.toLowerCase()}-pod
`;

if (annotations.length > 0) {
code += ` annotations:\n${annotations.join('\n')}\n`;
}

code += `spec:
containers:
- name: hami-container
image: ubuntu:22.04
command: ["sleep", "infinity"]
resources:
limits:
${limits.join('\n')}`;

return code;
}, [vendor, deviceCount, memMode, memValue, coreMode, coreValue, advanced, deviceType, deviceUuid]);

const vInfo = VENDORS[vendor];

return (
<div className={styles.generatorContainer}>
<div className={styles.controlsPanel}>
<h3>
<Translate id="manifest.generator.title">Resource Request Configuration</Translate>
</h3>

<div className={styles.inputGroup}>
<label htmlFor="deviceVendor">
<Translate id="manifest.generator.vendor">Device Vendor</Translate>
</label>
<select id="deviceVendor" value={vendor} onChange={(e) => {
setVendor(e.target.value);
setMemMode(VENDORS[e.target.value].memUnit === '%' ? 'percentage' : 'value');
}} className={styles.select}>
Comment on lines +148 to +151

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reset an unsupported core allocation mode after a vendor change.

If a user selects NVIDIA percentage cores and then selects Hygon or Iluvatar, coreMode remains percentage. The UI shows a core value, but the generator emits no core limit because the new vendor has no corePctKey. Reset coreMode to none when the next vendor does not support the selected mode.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ManifestGenerator/index.js` around lines 148 - 151, Update the
deviceVendor select onChange handler to reset coreMode to none when the newly
selected vendor lacks the key required by the currently selected core allocation
mode, while preserving supported modes and the existing vendor/memory-mode
updates.

{Object.entries(VENDORS).map(([key, val]) => (
<option key={key} value={key}>{val.name}</option>
))}
</select>
</div>

<div className={styles.inputGroup}>
<label htmlFor="deviceCount">
<Translate id="manifest.generator.deviceCount">Number of Devices Requested</Translate>
</label>
<input id="deviceCount" type="number" min="1" value={deviceCount} onChange={e => setDeviceCount(Math.max(1, parseInt(e.target.value) || 1))} className={styles.input} />
</div>

{vInfo.memKey && (
<div className={styles.flexRow}>
<div className={styles.inputGroup}>
<label htmlFor="memMode">
<Translate id="manifest.generator.memMode">Memory Allocation Type</Translate>
</label>
<select id="memMode" value={memMode} onChange={e => setMemMode(e.target.value)} className={styles.select} disabled={vInfo.memUnit === '%'}>
{vInfo.memUnit !== '%' && (
<option value="value">
{translate({ id: 'manifest.generator.memMode.absolute', message: 'Absolute (MiB)' })}
</option>
)}
{(vInfo.memPctKey || vInfo.memUnit === '%') && (
<option value="percentage">
{translate({ id: 'manifest.generator.memMode.percentage', message: 'Percentage (%)' })}
</option>
)}
</select>
</div>
<div className={styles.inputGroup}>
<label htmlFor="memValue">
<Translate id="manifest.generator.memValue">Memory Value</Translate>
</label>
<input id="memValue" type="number" min="0" value={memValue} onChange={e => setMemValue(Math.max(0, parseInt(e.target.value) || 0))} className={styles.input} />
</div>
</div>
)}

{vInfo.coreKey && (
<div className={styles.flexRow}>
<div className={styles.inputGroup}>
<label htmlFor="coreMode">
<Translate id="manifest.generator.coreMode">Core Allocation</Translate>
</label>
<select id="coreMode" value={coreMode} onChange={e => setCoreMode(e.target.value)} className={styles.select}>
<option value="none">
{translate({ id: 'manifest.generator.coreMode.none', message: 'None (Default)' })}
</option>
<option value="value">
{translate({ id: 'manifest.generator.coreMode.absolute', message: 'Absolute Cores' })}
</option>
{vInfo.corePctKey && (
<option value="percentage">
{translate({ id: 'manifest.generator.coreMode.percentage', message: 'Percentage (%)' })}
</option>
)}
</select>
</div>
{coreMode !== 'none' && (
<div className={styles.inputGroup}>
<label htmlFor="coreValue">
<Translate id="manifest.generator.coreValue">Core Value</Translate>
</label>
<input id="coreValue" type="number" min="0" value={coreValue} onChange={e => setCoreValue(Math.max(0, parseInt(e.target.value) || 0))} className={styles.input} />
</div>
)}
</div>
)}

<button type="button" aria-expanded={advanced} className={clsx(styles.advancedToggle, styles.interactiveText)} onClick={() => setAdvanced(!advanced)} style={{ background: 'none', border: 'none', padding: 0 }}>
<span>
{advanced ? '▼ ' : '▶ '}
<Translate id="manifest.generator.advanced">Advanced Configurations (Device Type / UUID)</Translate>
</span>
</button>

{advanced && vInfo.typeKey && (
<div className={styles.inputGroup}>
<label htmlFor="deviceType">
<Translate id="manifest.generator.deviceType">Specific Device Type Constraints (e.g. NVIDIA-A100)</Translate>
</label>
<input id="deviceType" type="text" value={deviceType} onChange={e => setDeviceType(e.target.value)} placeholder={translate({ id: 'manifest.generator.emptyPlaceholder', message: 'Leave empty for any' })} className={styles.input} />
</div>
)}

{advanced && vInfo.uuidKey && (
<div className={styles.inputGroup}>
<label htmlFor="deviceUuid">
<Translate id="manifest.generator.deviceUuid">Specific Device UUID (e.g. GPU-fef808...)</Translate>
</label>
<input id="deviceUuid" type="text" value={deviceUuid} onChange={e => setDeviceUuid(e.target.value)} placeholder={translate({ id: 'manifest.generator.emptyPlaceholder', message: 'Leave empty for any' })} className={styles.input} />
</div>
)}

</div>

<div className={styles.previewPanel}>
<h3>
<Translate id="manifest.generator.previewTitle">Generated YAML Manifest</Translate>
</h3>
<p>
<Translate id="manifest.generator.previewDesc">Integrate this into your Kubernetes Pod or Deployment spec.</Translate>
</p>
Comment on lines +255 to +257

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not describe a Pod manifest as directly usable in a Deployment spec.

The generated YAML declares kind: Pod. A Deployment requires a spec.template and cannot accept this manifest unchanged. Add a workload selector that generates Deployment YAML, or state that users must move the container resources into spec.template.spec.

  • src/components/ManifestGenerator/index.js#L255-L257: Correct the preview instruction or add Deployment output.
  • docs/userguide/interactive-manifest-generator.md#L10-L10: Describe the required Deployment template conversion.
  • i18n/zh/docusaurus-plugin-content-docs/current/userguide/interactive-manifest-generator.md#L10-L10: Mirror the corrected English instruction.
📍 Affects 3 files
  • src/components/ManifestGenerator/index.js#L255-L257 (this comment)
  • docs/userguide/interactive-manifest-generator.md#L10-L10
  • i18n/zh/docusaurus-plugin-content-docs/current/userguide/interactive-manifest-generator.md#L10-L10
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ManifestGenerator/index.js` around lines 255 - 257, Correct
the ManifestGenerator preview text so the generated kind: Pod YAML is not
presented as directly usable in a Deployment; either add a Deployment output
mode or explicitly instruct users to place the container resources under
spec.template.spec. Update the corresponding guidance in
docs/userguide/interactive-manifest-generator.md at line 10 and mirror the
correction in
i18n/zh/docusaurus-plugin-content-docs/current/userguide/interactive-manifest-generator.md
at line 10.

<CodeBlock language="yaml" title="pod.yaml">
{yamlCode}
</CodeBlock>
</div>
</div>
);
}
Loading