Repository navigation
feat(ascend,hygon): register AI-core/dcucores and support new and legacy vnpus config #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
hami-robot
merged 8 commits into
Project-HAMi:master
from
Wangmin362:feat/support-new-npu-template
Jul 7, 2026
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
0f21b32
fix(hygon): run device.Register in a goroutine so the DCU manager starts
f30d9cb
feat(ascend): register AI-core resource and support new+legacy vnpus …
eb34ad4
feat(hygon): register dcucores resource
62aceda
chore(build): support GOPROXY build-arg for faster image builds
e138184
docs,build: address review feedback
b996c3a
fix(ascend): report core as a percentage (100 per physical card)
b50e682
fix(ascend): report -core only when node is in hami-vnpu-core mode
44e4ebb
test(ascend): cover HamiVnpuCore wiring in InitDevices
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # Makefile for HAMi mock-device-plugin | ||
| # | ||
| # Run `make help` to see the available targets. | ||
|
|
||
| # ---- Variables (override on the command line, e.g. `make docker-build IMG=myrepo/mock:dev`) ---- | ||
| REGISTRY ?= projecthami | ||
| IMAGE_NAME ?= mock-device-plugin | ||
| TAG ?= latest | ||
| IMG ?= $(REGISTRY)/$(IMAGE_NAME):$(TAG) | ||
|
|
||
| BINARY ?= k8s-device-plugin | ||
| MAIN_PKG ?= ./cmd/k8s-device-plugin | ||
| OUTPUT_DIR ?= bin | ||
|
|
||
| GIT_DESCRIBE ?= $(shell git describe --always --long --dirty 2>/dev/null || echo "unknown") | ||
| LDFLAGS ?= -X main.gitDescribe=$(GIT_DESCRIBE) | ||
|
|
||
| GO ?= go | ||
| DOCKER ?= docker | ||
| # Use `make ... DOCKER=nerdctl` on containerd-only hosts. | ||
|
|
||
| # Optional Go module proxy passed into the image build. Set it for faster builds | ||
| # behind a slow network, e.g. `make docker-build GOPROXY=https://goproxy.cn,direct`. | ||
| GOPROXY ?= | ||
| DOCKER_BUILD_ARGS := | ||
| ifneq ($(GOPROXY),) | ||
| DOCKER_BUILD_ARGS += --build-arg GOPROXY=$(GOPROXY) | ||
| endif | ||
|
|
||
| # Detect golangci-lint availability for the lint target. | ||
| GOLANGCI_LINT := $(shell command -v golangci-lint 2>/dev/null) | ||
|
|
||
| .DEFAULT_GOAL := help | ||
|
|
||
| .PHONY: all | ||
| all: build ## Alias for `build` (compatibility with standard Makefile tooling). | ||
|
|
||
| ##@ General | ||
|
|
||
| .PHONY: help | ||
| help: ## Display this help. | ||
| @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-18s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST) | ||
|
|
||
| ##@ Development | ||
|
|
||
| .PHONY: fmt | ||
| fmt: ## Run go fmt against code. | ||
| $(GO) fmt ./... | ||
|
|
||
| .PHONY: vet | ||
| vet: ## Run go vet against code. | ||
| $(GO) vet ./... | ||
|
|
||
| .PHONY: tidy | ||
| tidy: ## Tidy go.mod / go.sum. | ||
| $(GO) mod tidy | ||
|
|
||
| .PHONY: lint | ||
| lint: ## Run golangci-lint (if installed). | ||
| ifeq ($(GOLANGCI_LINT),) | ||
| @echo "golangci-lint not found; install: https://golangci-lint.run/usage/install/ -- skipping." | ||
| else | ||
| golangci-lint run ./... | ||
| endif | ||
|
|
||
| .PHONY: test | ||
| test: ## Run unit tests. | ||
| $(GO) test ./... | ||
|
|
||
| .PHONY: test-verbose | ||
| test-verbose: ## Run unit tests with verbose output. | ||
| $(GO) test -v ./... | ||
|
|
||
| .PHONY: cover | ||
| cover: ## Run unit tests and report coverage. | ||
| $(GO) test -coverprofile=coverage.out ./... | ||
| $(GO) tool cover -func=coverage.out | ||
|
|
||
| ##@ Build | ||
|
|
||
| .PHONY: build | ||
| build: vet ## Build the device-plugin binary into bin/. (run `make fmt` separately to format) | ||
| mkdir -p $(OUTPUT_DIR) | ||
| CGO_ENABLED=0 $(GO) build -ldflags "$(LDFLAGS)" -o $(OUTPUT_DIR)/$(BINARY) $(MAIN_PKG) | ||
|
|
||
| .PHONY: clean | ||
| clean: ## Remove build artifacts. | ||
| rm -rf $(OUTPUT_DIR) coverage.out | ||
|
|
||
| ##@ Container | ||
|
|
||
| .PHONY: docker-build | ||
| docker-build: ## Build the container image ($(IMG)). Set GOPROXY=... for a faster module proxy. | ||
| $(DOCKER) build $(DOCKER_BUILD_ARGS) -t $(IMG) . | ||
|
|
||
| .PHONY: docker-push | ||
| docker-push: ## Push the container image ($(IMG)). | ||
| $(DOCKER) push $(IMG) | ||
|
|
||
| ##@ Deployment (requires kubectl + a valid KUBECONFIG) | ||
|
|
||
| .PHONY: deploy | ||
| deploy: ## Deploy RBAC + DaemonSet to the current cluster. | ||
| kubectl apply -f k8s-mock-rbac.yaml | ||
| kubectl apply -f k8s-mock-plugin.yaml | ||
|
|
||
| .PHONY: undeploy | ||
| undeploy: ## Remove the DaemonSet + RBAC from the current cluster. | ||
| kubectl delete -f k8s-mock-plugin.yaml --ignore-not-found | ||
| kubectl delete -f k8s-mock-rbac.yaml --ignore-not-found | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,56 +1,175 @@ | ||
| # Mock device plugin for HAMi | ||
| [](https://app.fossa.com/projects/git%2Bgithub.com%2FProject-HAMi%2Fmock-device-plugin?ref=badge_shield) | ||
|
|
||
|
|
||
| ## Introduction | ||
|
|
||
| This is a Kubernetes device plugin implementation that enables the registration of virtual-devices which would normally be ignored by scheduler (i.e gpu-memory, gpu-cores, etc..)on each node. After deployment, these resources will be available on `node.status.allocatable` and `node.status.capacity` | ||
| This is a Kubernetes device plugin that registers **virtual** device resources (e.g. `gpu-memory`, `gpu-cores`) -- the resources HAMi tracks but the kubelet would normally ignore -- onto a node, **without requiring real hardware**. It lets you test the HAMi scheduler (scheduling policies, quotas, webhooks) on machines that have no GPU/NPU. | ||
|
|
||
| After deployment these resources show up under `node.status.allocatable` and `node.status.capacity`. | ||
|
|
||
| ## How it works (read this first) | ||
|
|
||
| The mock plugin **does not detect hardware**. It does the following every ~30s: | ||
|
|
||
| ```text | ||
| node annotation: hami.io/node-<vendor>-register = [ {devmem, devcore, ...}, ... ] (1) | ||
| node capacity: <count resource> (e.g. nvidia.com/gpu) > 0 (2) <- health gate | ||
| | mock reads | ||
| v | ||
| registers into allocatable: <vendor>/...mem , <vendor>/...cores , ... | ||
| ``` | ||
|
|
||
| On a **real** cluster, (1) and (2) are produced by the real device plugin. In a **mock-only** (no hardware) environment you provide them yourself: | ||
|
|
||
| - **(1) the `node-<vendor>-register` annotation** describing the fake cards -- `kubectl annotate`. | ||
| - **(2) the count extended resource** (e.g. `nvidia.com/gpu`) -- patched onto the node `status`. | ||
|
|
||
| > There is **no auto-labeller** in this repo, so (1) and (2) are manual today. Forgetting them is the usual cause of `device xxx is unhealthy` / `no allocation` -- see issues #14 / #16. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Kubernetes version >= v1.18 | ||
| - Kubernetes >= v1.18 | ||
| - The `hami-scheduler-device` ConfigMap (the device config). If HAMi is installed it already exists; otherwise create it from [device-configmap.yaml](https://github.com/Project-HAMi/HAMi/blob/master/charts/hami/templates/scheduler/device-configmap.yaml). | ||
|
|
||
| ## Deployment | ||
|
|
||
| If the `hami-scheduler-device` ConfigMap is not deployed, it needs to be deployed first. refer [device-configmap.yaml](https://github.com/Project-HAMi/HAMi/blob/master/charts/hami/templates/scheduler/device-configmap.yaml) | ||
|
|
||
| ```bash | ||
| make deploy # = kubectl apply -f k8s-mock-rbac.yaml && kubectl apply -f k8s-mock-plugin.yaml | ||
| # or manually: | ||
| kubectl apply -f k8s-mock-rbac.yaml | ||
| kubectl apply -f k8s-mock-plugin.yaml | ||
| ``` | ||
| $ kubectl apply -f k8s-mock-rbac.yaml | ||
| $ kubectl apply -f k8s-mock-plugin.yaml | ||
|
|
||
| ## Understanding the values | ||
|
|
||
| The mock **derives the registered resources from the annotation, not from the count resource.** This trips people up, so to be explicit: | ||
|
|
||
| - **Number of fake cards = the number of entries in the annotation array** (not the count value). | ||
| - Registered `...-memory` = **sum of `devmem`** over all entries. | ||
| - Registered `...-cores` / `...-core` = **sum of `devcore`** over all entries. | ||
| - The **count extended resource is only a health gate**: its value just needs to be `> 0`. It does **not** affect the registered memory/cores. By convention it is set to `cards x splits-per-card` (e.g. Ascend `2 x VDeviceCount(4) = 8`), but `1` would work equally well for the memory/cores to appear. | ||
|
|
||
| Annotation entry fields: | ||
|
|
||
| | field | meaning | | ||
| | :-- | :-- | | ||
| | `id` | unique device UUID (any string) | | ||
| | `devmem` | per-card memory in MB -- **summed** into `...-memory` | | ||
| | `devcore` | per-card cores. **NVIDIA/Hygon:** summed into `...-cores` (NVIDIA: percentage, 100 = a whole card). **Ascend:** ignored -- `huawei.com/<chip>-core` is percentage-based, registered as **100 per card**. | | ||
| | `count` | per-card split count (informational for the mock) | | ||
| | `type` | device model string | | ||
| | `health` | must be `true` to be counted | | ||
| | `index` | card index `0,1,2,...` (`0` may be omitted) | | ||
| | `numa`, `mode` | optional | | ||
|
|
||
| > **Worked example (Ascend, below):** the annotation has **2 entries**, each `devmem=32768`. So `...-memory = 2x32768 = 65536` and `...-core = 2x100 = 200` (Ascend `...-core` is **percentage-based**: a whole card is always **100**). The count resource `=8` is a separate health-gate value and is unrelated to these numbers. | ||
|
|
||
| ## Usage by vendor | ||
|
|
||
| To mock one card you always need the **three pieces**: the vendor **config block** (in the `hami-scheduler-device` ConfigMap, gives the resource names), the **count extended resource** (passes the health gate), and the **node-register annotation** (describes the fake cards). Replace `<node>` below with your target node. Resource names follow the ConfigMap (HAMi defaults shown). See [Understanding the values](#understanding-the-values) for how the numbers are derived. | ||
|
|
||
| ### NVIDIA (e.g. A100-80GB) | ||
|
|
||
| - config block: `nvidia:` | annotation: `hami.io/node-nvidia-register` (JSON) | count: `nvidia.com/gpu` | ||
| - mock registers: `nvidia.com/gpumem`, `nvidia.com/gpucores`, `nvidia.com/gpumem-percentage` | ||
|
|
||
| ```bash | ||
| # (2) count resource: splitCount=10 -> one card advertises 10 | ||
| kubectl patch node <node> --subresource=status --type=json \ | ||
| -p '[{"op":"add","path":"/status/capacity/nvidia.com~1gpu","value":"10"}]' | ||
| # (1) annotation: 1 x A100-80G (devmem=81920 MB, devcore=100) | ||
| kubectl annotate node <node> \ | ||
| 'hami.io/node-nvidia-register=[{"id":"GPU-MOCK-0","count":10,"devmem":81920,"devcore":100,"type":"NVIDIA-A100-SXM4-80GB","health":true,"numa":0,"mode":"hami-core"}]' | ||
| # verify (~30s later) | ||
| kubectl get node <node> -o json | jq '.status.allocatable|with_entries(select(.key|test("nvidia.com")))' | ||
| # expect: nvidia.com/gpu=10, nvidia.com/gpumem=81920, nvidia.com/gpucores=100, nvidia.com/gpumem-percentage=100 | ||
| ``` | ||
|
|
||
| ## Build | ||
| ### Ascend NPU (e.g. 910B4) | ||
|
|
||
| - config block: `vnpus.configs` (entry for `910B4`) | annotation: `hami.io/node-register-Ascend910B4` (JSON) | count: `huawei.com/Ascend910B4` | ||
| - mock registers: `huawei.com/Ascend910B4-memory`, and -- **only when the node runs in `hami-vnpu-core` soft mode** (see below) -- `huawei.com/Ascend910B4-core` | ||
|
|
||
| ```bash | ||
| # (2) count resource: 2 cards x VDeviceCount(4) = 8 | ||
| kubectl patch node <node> --subresource=status --type=json \ | ||
| -p '[{"op":"add","path":"/status/capacity/huawei.com~1Ascend910B4","value":"8"}]' | ||
| # (1) annotation: 2 x 910B4 (devmem=32768 MB, devcore=20), matching the real ascend-device-plugin report | ||
| kubectl annotate node <node> \ | ||
| 'hami.io/node-register-Ascend910B4=[{"id":"MOCK-0","count":4,"devmem":32768,"devcore":20,"type":"Ascend910B4","health":true},{"id":"MOCK-1","index":1,"count":4,"devmem":32768,"devcore":20,"type":"Ascend910B4","health":true}]' | ||
| # (1b) soft mode: mark the node as hami-vnpu-core so the -core resource is reported. | ||
| # Either set vnpus.hamiVnpuCore: true globally in the ConfigMap, or annotate this node | ||
| # (the node annotation overrides the global setting, same precedence as the real plugin): | ||
| kubectl annotate node <node> hami-vnpu-core=true | ||
| # verify | ||
| kubectl get node <node> -o json | jq '.status.allocatable|with_entries(select(.key|test("Ascend910B4")))' | ||
| # expect: huawei.com/Ascend910B4-memory=65536, huawei.com/Ascend910B4-core=200 (2 cards x 100) | ||
| ``` | ||
| docker build . | ||
|
|
||
| The Ascend `-core` resource is **percentage-based**: each physical card contributes **100** (a whole card), independent of the annotation's `devcore`. HAMi caps a core request at 100 and, in `hami-vnpu-core` soft mode, treats a card's total core as 100. | ||
|
|
||
| **When is `-core` reported?** Only on a node that runs in `hami-vnpu-core` (soft-partition) mode, mirroring the real ascend-device-plugin and the HAMi scheduler. Mode is decided by: the node's `hami-vnpu-core` annotation if present, otherwise the global `vnpus.hamiVnpuCore` in the ConfigMap (`true`/`false`, default `false`). On a hard/template node (`hami-vnpu-core` off) the HAMi scheduler filters out any pod that requests `-core` (`ModeNotFit`), so the mock does not register `-core` there. There is **no ascend-device-plugin** in a mock environment, so nothing writes the `hami-vnpu-core` node annotation automatically -- set it yourself (like the `node-register` annotation and the count resource). | ||
|
|
||
| ### Hygon DCU | ||
|
|
||
| - config block: `hygon:` | annotation: `hami.io/node-dcu-register` (**CSV**, not JSON) | count: `hygon.com/dcunum` | ||
| - mock registers: `hygon.com/dcumem`, and -- when the config sets `resourceCoreName` -- `hygon.com/dcucores` (sum of per-card `devcore`) | ||
|
|
||
| ```bash | ||
| # (2) count resource: 2 DCUs | ||
| kubectl patch node <node> --subresource=status --type=json \ | ||
| -p '[{"op":"add","path":"/status/capacity/hygon.com~1dcunum","value":"2"}]' | ||
| # (1) annotation: CSV form is "id,count,devmem,devcore,type,numa,health,index,mode:" per card | ||
| kubectl annotate node <node> \ | ||
| 'hami.io/node-dcu-register=DCU-MOCK-0,2,16384,100,Z100L,0,true,0,hami-core:DCU-MOCK-1,2,16384,100,Z100L,1,true,1,hami-core:' | ||
| # verify | ||
| kubectl get node <node> -o json | jq '.status.allocatable|with_entries(select(.key|test("hygon.com")))' | ||
| # expect: hygon.com/dcumem=32768, hygon.com/dcucores=200 (2 cards x devcore 100) | ||
| ``` | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
|
|
||
| ## Examples | ||
| ## Ascend config compatibility (new vs legacy) | ||
|
|
||
| The Ascend `vnpus` config has two layouts and the plugin accepts **both**: | ||
|
|
||
| ```yaml | ||
| # New (HAMi >= v2.9.0): nested object | ||
| vnpus: | ||
| hamiVnpuCore: true # node runs in soft mode -> the -core resource is reported | ||
| configs: | ||
| - chipName: 910B4 | ||
| resourceCoreName: huawei.com/Ascend910B4-core # name of the -core resource | ||
| ... | ||
| ``` | ||
| Allocatable: | ||
| ... | ||
| memory: 769189866507 | ||
| nvidia.com/gpu: 20 | ||
| nvidia.com/gpucores: 200 | ||
| nvidia.com/gpumem: 65536 | ||
| nvidia.com/gpumem-percentage: 200 | ||
| pods: 110 | ||
| ... | ||
| ```yaml | ||
| # Legacy (older / downstream): a bare list | ||
| vnpus: | ||
| - chipName: 910B4 | ||
| ... | ||
| ``` | ||
| The new nested format is tried first; if that fails it falls back to the legacy flat list; if neither matches an error is returned. This lets clusters that have not upgraded their ConfigMap keep working. | ||
|
|
||
| ## ManagedResources | ||
|
|
||
| | Devices | Mocking Resources | | ||
| | :--- | :----: | | ||
| | Nvidia GPU | nvidia.com/gpumem, nvidia.com/gpumem-percentage, nvidia.com/gpucores | | ||
| | Hygon DCU | hygon.com/dcumem | | ||
| | Ascend | huawei.com/Ascend{chip-name}-memory | | ||
| | Devices | Mocking Resources | | ||
| | :--- | :---- | | ||
| | Nvidia GPU | `nvidia.com/gpumem`, `nvidia.com/gpumem-percentage`, `nvidia.com/gpucores` | | ||
| | Hygon DCU | `hygon.com/dcumem`, `hygon.com/dcucores` (when `resourceCoreName` is set) | | ||
| | Ascend | `huawei.com/Ascend{chip}-memory`, `huawei.com/Ascend{chip}-core` (when `resourceCoreName` is set **and** the node is in `hami-vnpu-core` mode) | | ||
|
|
||
| **Note:** If the counted memory is too large, for example exceeding 120GB, it will display as 0. In this case, you can set the `memoryFactor` in `hami-scheduler-device` ConfigMap. The default value of `memoryFactor` is 1. | ||
| **Note:** If the counted memory is too large (e.g. > 120GB) it may display as 0. Set `memoryFactor` in the `hami-scheduler-device` ConfigMap (default 1). | ||
|
|
||
| ## Build | ||
|
|
||
| ```bash | ||
| make build # build the binary into bin/ | ||
| make test # run unit tests | ||
| make docker-build # build the image (override: IMG=myrepo/mock:tag) | ||
| make help # list all targets | ||
| ``` | ||
|
|
||
| ## Maintainer | ||
|
|
||
| limengxuan@4paradigm.com | ||
|
|
||
| ## License | ||
| [](https://app.fossa.com/projects/git%2Bgithub.com%2FProject-HAMi%2Fmock-device-plugin?ref=badge_large) | ||
| [](https://app.fossa.com/projects/git%2Bgithub.com%2FProject-HAMi%2Fmock-device-plugin?ref=badge_large) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.