vdi-cloud/platform/pkg/proxmox/gpu_test.go

79 lines
3.1 KiB
Go

package proxmox
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
func TestPCIAddressAndHostPCIParsing(t *testing.T) {
for _, value := range []string{"01:00.0", "0000:01:00.0", " 0000:01:00.0 "} {
got, ok := NormalizePCIAddress(value)
if !ok || got != "0000:01:00.0" {
t.Fatalf("normalization %q: %q %v", value, got, ok)
}
}
for _, value := range []string{"01:20.0", "01:00.8", "../../config", "01:00.0,pcie=1", "000000:01:00.0"} {
if _, ok := NormalizePCIAddress(value); ok {
t.Fatalf("accepted invalid address %q", value)
}
}
if !PCIAddressMatches("01:00", "0000:01:00.1") || PCIAddressMatches("01:00.0", "0000:01:00.1") || PCIAddressMatches("0001:01:00", "0000:01:00.0") {
t.Fatal("function/domain matching incorrect")
}
parsed, err := ParseHostPCI("host=01:00;0001:02:00.0,pcie=1,x-vga=1")
if err != nil || len(parsed.Addresses) != 2 || parsed.Addresses[0] != "0000:01:00" {
t.Fatalf("parse multi-function PCI: %+v %v", parsed, err)
}
parsed, err = ParseHostPCI("mapping=gpu-pool,mdev=nvidia-1")
if err != nil || parsed.Mapping != "gpu-pool" || !parsed.Mediated {
t.Fatalf("parse mediated mapping: %+v %v", parsed, err)
}
for _, value := range []string{"", "mapping=x,host=01:00", "host=01:00,host=02:00", "host=invalid", "mapping=", "pcie=1"} {
if _, err := ParseHostPCI(value); err == nil {
t.Fatalf("accepted invalid hostpci %q", value)
}
}
}
func TestGPUReadOnlyPVEContract(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
t.Errorf("unexpected write: %s", r.Method)
}
var data any
switch r.URL.Path {
case "/api2/json/nodes/node-a/hardware/pci":
if r.URL.Query().Get("verbose") != "1" {
t.Error("missing verbose PCI properties")
}
data = []map[string]any{{"id": "0000:00:02.0", "class": "0x030000", "iommugroup": 0}, {"id": "0000:01:00.1", "class": "0x040300", "iommugroup": 2}}
case "/api2/json/nodes/node-a/qemu/8000/pending":
data = []map[string]any{{"key": "hostpci0", "value": "01:00.0", "pending": "02:00.0", "delete": 1}}
case "/api2/json/cluster/mapping/pci":
data = []map[string]any{{"id": "gpu-map", "map": []string{"node=node-a,path=0000:01:00.0,id=10de:1234"}}}
default:
t.Errorf("unexpected path: %s", r.URL.Path)
http.NotFound(w, r)
return
}
_ = json.NewEncoder(w).Encode(map[string]any{"data": data})
}))
defer srv.Close()
c := &Client{BaseURL: srv.URL, HTTP: srv.Client()}
devices, err := c.PCIDevices(context.Background(), "node-a")
if err != nil || len(devices) != 2 || !devices[0].IsGPU() || devices[1].IsGPU() || devices[0].IOMMUGroup == nil || *devices[0].IOMMUGroup != 0 {
t.Fatalf("PCI decode: %+v %v", devices, err)
}
pending, err := c.VMPending(context.Background(), "node-a", 8000)
if err != nil || len(pending) != 1 || string(pending[0].Delete) != "1" {
t.Fatalf("pending decode: %+v %v", pending, err)
}
mappings, err := c.PCIMappings(context.Background())
if err != nil || len(mappings) != 1 || len(mappings[0].Map) != 1 {
t.Fatalf("mapping decode: %+v %v", mappings, err)
}
}