79 lines
3.1 KiB
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)
|
|
}
|
|
}
|