diff --git a/packages/web/projects/vgpu/views/task/admin/Detail.vue b/packages/web/projects/vgpu/views/task/admin/Detail.vue
index 4d43f8a..15785fb 100644
--- a/packages/web/projects/vgpu/views/task/admin/Detail.vue
+++ b/packages/web/projects/vgpu/views/task/admin/Detail.vue
@@ -150,23 +150,18 @@
diff --git a/server/internal/exporter/exporter.go b/server/internal/exporter/exporter.go
index 636831d..3e2ad2b 100644
--- a/server/internal/exporter/exporter.go
+++ b/server/internal/exporter/exporter.go
@@ -399,10 +399,13 @@ func (s *MetricsGenerator) GenerateContainerMetrics(ctx context.Context) error {
case biz.HygonGPUDevice:
used = float64(taskCoreUsed)
util = roundToOneDecimal(100 * float64(taskCoreUsed) / float64(core))
- case metax.MetaxSGPUDevice:
- used = float64(taskCoreUsed)
- util = roundToOneDecimal(100 * float64(taskCoreUsed) / float64(core))
- default:
+ case biz.AscendGPUDevice:
+ used = float64(taskCoreUsed) / 100 * float64(core)
+ util = float64(taskCoreUsed)
+ case metax.MetaxSGPUDevice:
+ used = float64(taskCoreUsed)
+ util = roundToOneDecimal(100 * float64(taskCoreUsed) / float64(core))
+ default:
}
cardCoreUtil, err := s.deviceCoreUtil(ctx, provider, device.Id)
if err == nil && used != 0 && cardCoreUtil > 95 {
@@ -415,6 +418,9 @@ func (s *MetricsGenerator) GenerateContainerMetrics(ctx context.Context) error {
taskMemoryUsed, err := s.taskMemoryUsed(ctx, provider, c.Namespace, c.PodName, c.Name, c.PodUID, device.Id, device.NodeName, device.Index)
if err == nil {
switch provider {
+ case biz.AscendGPUDevice:
+ // npu_chip_info_hbm_used_memory returns MB (10^6 bytes), convert to bytes for downstream /1024/1024 → MiB
+ taskMemoryUsed = float32(float64(taskMemoryUsed) * 1000 * 1000)
case biz.CambriconGPUDevice:
taskMemoryUsed = float32((taskMemoryUsed/100)*float32(memory)) * 1024 * 1024
case metax.MetaxSGPUDevice:
@@ -540,7 +546,7 @@ func (s *MetricsGenerator) taskCoreUsed(ctx context.Context, provider, namespace
case biz.CambriconGPUDevice:
query = fmt.Sprintf("avg(mlu_utilization * on(uuid) group_right mlu_container{namespace=\"%s\",pod=\"%s\",container=\"%s\",type=\"mlu370.smlu.vcore\"})", namespace, pod, container)
case biz.AscendGPUDevice:
- return 0, nil
+ query = fmt.Sprintf("avg(npu_chip_info_utilization{vdie_id=\"%s\"})", deviceUUID)
case biz.HygonGPUDevice:
query = fmt.Sprintf("avg(vdcu_percent{pod_uuid=\"%s\", container_name=\"%s\"})", podUUID, container)
case biz.MetaxGPUDevice, metax.MetaxGPUDevice:
@@ -563,7 +569,7 @@ func (s *MetricsGenerator) taskMemoryUsed(ctx context.Context, provider, namespa
case biz.CambriconGPUDevice:
query = fmt.Sprintf("avg(mlu_memory_utilization * on(uuid) group_right mlu_container{namespace=\"%s\",pod=\"%s\",container=\"%s\",type=\"mlu370.smlu.vmemory\"})", namespace, pod, container)
case biz.AscendGPUDevice:
- return 0, nil
+ query = fmt.Sprintf("avg(npu_chip_info_hbm_used_memory{vdie_id=\"%s\"})", deviceUUID)
case biz.HygonGPUDevice:
query = fmt.Sprintf("avg(vdcu_usage_memory_size{pod_uuid=\"%s\", container_name=\"%s\"})", podUUID, container)
case metax.MetaxGPUDevice:
diff --git a/server/internal/exporter/exporter_test.go b/server/internal/exporter/exporter_test.go
new file mode 100644
index 0000000..9a70488
--- /dev/null
+++ b/server/internal/exporter/exporter_test.go
@@ -0,0 +1,101 @@
+package exporter
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+ "time"
+
+ "vgpu/internal/biz"
+ "vgpu/internal/data/prom"
+ "vgpu/internal/service"
+
+ "github.com/go-kratos/kratos/v2/log"
+)
+
+// testPromHandler returns a handler that acts as a fake Prometheus /api/v1/query
+// endpoint. When value is non-empty, it returns a single-sample vector with that
+// value. When value is empty, it returns an empty result vector.
+func testPromHandler(t *testing.T, value string) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost && r.Method != http.MethodGet {
+ t.Errorf("unexpected method: %s", r.Method)
+ }
+ if !strings.HasSuffix(r.URL.Path, "/api/v1/query") {
+ t.Errorf("unexpected path: %s", r.URL.Path)
+ }
+ w.Header().Set("Content-Type", "application/json")
+ if value == "" {
+ fmt.Fprint(w, `{"status":"success","data":{"resultType":"vector","result":[]}}`)
+ return
+ }
+ fmt.Fprintf(w, `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1719500000,%q]}]}}`, value)
+ }
+}
+
+// newTestGenerator starts an httptest server with the given handler, creates a
+// prom.Client pointed at it, wraps it in a MonitorService, and returns a bare
+// MetricsGenerator (other fields left nil). The returned close func shuts down
+// the test server.
+func newTestGenerator(t *testing.T, handler http.HandlerFunc) (*MetricsGenerator, func()) {
+ srv := httptest.NewServer(handler)
+
+ promClient, err := prom.NewClient(srv.URL, time.Second, "")
+ if err != nil {
+ srv.Close()
+ t.Fatalf("prom.NewClient: %v", err)
+ }
+
+ monitorSvc := service.NewMonitorService(promClient, nil, nil)
+
+ gen := &MetricsGenerator{
+ monitorService: monitorSvc,
+ log: log.NewHelper(log.NewStdLogger(io.Discard)),
+ }
+
+ return gen, srv.Close
+}
+
+func TestMetricsGenerator_taskCoreUsed_Ascend(t *testing.T) {
+ gen, cleanup := newTestGenerator(t, testPromHandler(t, "42.5"))
+ defer cleanup()
+
+ result, err := gen.taskCoreUsed(context.Background(), biz.AscendGPUDevice, "ns", "pod", "ctr", "pod-uuid", "dev-uuid", "host", 0)
+ if err != nil {
+ t.Fatalf("taskCoreUsed failed: %v", err)
+ }
+ if result != 42.5 {
+ t.Errorf("expected 42.5, got %v", result)
+ }
+}
+
+func TestMetricsGenerator_taskMemoryUsed_Ascend(t *testing.T) {
+ // npu_chip_info_hbm_used_memory returns MB, e.g., 8192 for 8 GB HBM
+ gen, cleanup := newTestGenerator(t, testPromHandler(t, "8192"))
+ defer cleanup()
+
+ result, err := gen.taskMemoryUsed(context.Background(), biz.AscendGPUDevice, "ns", "pod", "ctr", "pod-uuid", "dev-uuid", "host", 0)
+ if err != nil {
+ t.Fatalf("taskMemoryUsed failed: %v", err)
+ }
+ if result != 8192 {
+ t.Errorf("expected 8192 (MB), got %v", result)
+ }
+}
+
+func TestMetricsGenerator_taskCoreUsed_Ascend_EmptyResult(t *testing.T) {
+ gen, cleanup := newTestGenerator(t, testPromHandler(t, ""))
+ defer cleanup()
+
+ result, err := gen.taskCoreUsed(context.Background(), biz.AscendGPUDevice, "ns", "pod", "ctr", "pod-uuid", "dev-uuid", "host", 0)
+ if err != nil {
+ t.Fatalf("taskCoreUsed failed: %v", err)
+ }
+ if result != 0 {
+ t.Errorf("expected 0, got %v", result)
+ }
+}