diff --git a/api/hyperv-winrm/vm_gpu_adapter.go b/api/hyperv-winrm/vm_gpu_adapter.go new file mode 100644 index 0000000..9301021 --- /dev/null +++ b/api/hyperv-winrm/vm_gpu_adapter.go @@ -0,0 +1,329 @@ +package hyperv_winrm + +import ( + "context" + "encoding/json" + "log" + "text/template" + + "github.com/taliesins/terraform-provider-hyperv/api" +) + +type createVmGpuAdapterArgs struct { + VMName string + VmGpuAdapterJson string +} + +var createVmGpuAdapterTemplate = template.Must(template.New("CreateVmGpuAdapter").Parse(` +$ErrorActionPreference = 'Stop' +$vmGpuAdapter = '{{.VmGpuAdapterJson}}' | ConvertFrom-Json + + +$NewVmGpuAdapterArgs = @{ + InstancePath=$vmGpuAdapter.InstancePath +} + +@(Get-VM -Name '{{.VMName}}') | Add-VMGpuPartitionAdapter @NewVmGpuAdapterArgs + +$SetVmGpuAdapterArgs = @{ + MinPartitionVRAM=$vmGpuAdapter.MinPartitionVRAM + MaxPartitionVRAM=$vmGpuAdapter.MaxPartitionVRAM + OptimalPartitionVRAM=$vmGpuAdapter.OptimalPartitionVRAM + + MinPartitionEncode=$vmGpuAdapter.MinPartitionEncode + MaxPartitionEncode=$vmGpuAdapter.MaxPartitionEncode + OptimalPartitionEncode=$vmGpuAdapter.OptimalPartitionEncode + + MinPartitionDecode=$vmGpuAdapter.MinPartitionDecode + MaxPartitionDecode=$vmGpuAdapter.MaxPartitionDecode + OptimalPartitionDecode=$vmGpuAdapter.OptimalPartitionDecode + + MinPartitionCompute=$vmGpuAdapter.MinPartitionCompute + MaxPartitionCompute=$vmGpuAdapter.MaxPartitionCompute + OptimalPartitionCompute=$vmGpuAdapter.OptimalPartitionCompute + +} + +@(Get-VM -Name '{{.VMName}}' | Get-VMGpuPartitionAdapter | ?{$_.InstancePath -eq $vmGpuAdapter.InstancePath }) | Set-VMGpuPartitionAdapter @SetVmGpuAdapterArgs + +`)) + +func (c *ClientConfig) CreateVmGpuAdapter( + ctx context.Context, + vmName string, + instancePath string, + minPartitionVram int32, + maxPartitionVram int32, + optimalPartitionVram int32, + minPartitionEncode int64, + maxPartitionEncode int64, + optimalPartitionEncode int64, + minPartitionDecode int32, + maxPartitionDecode int32, + optimalPartitionDecode int32, + minPartitionCompute int32, + maxPartitionCompute int32, + optimalPartitionCompute int32, +) (err error) { + vmGpuAdapterJson, err := json.Marshal(api.VmGpuAdapter{ + VmName: vmName, + InstancePath: instancePath, + MinPartitionVram: minPartitionVram, + MaxPartitionVram: maxPartitionVram, + OptimalPartitionVram: optimalPartitionVram, + MinPartitionEncode: minPartitionEncode, + MaxPartitionEncode: maxPartitionEncode, + OptimalPartitionEncode: optimalPartitionEncode, + MinPartitionDecode: minPartitionDecode, + MaxPartitionDecode: maxPartitionDecode, + OptimalPartitionDecode: optimalPartitionDecode, + MinPartitionCompute: minPartitionCompute, + MaxPartitionCompute: maxPartitionCompute, + OptimalPartitionCompute: optimalPartitionCompute, + }) + + if err != nil { + return err + } + + err = c.WinRmClient.RunFireAndForgetScript(ctx, createVmGpuAdapterTemplate, createVmGpuAdapterArgs{ + VMName: vmName, + VmGpuAdapterJson: string(vmGpuAdapterJson), + }) + + return err +} + +type getVmGpuAdaptersArgs struct { + VmName string +} + +var getVmGpuAdaptersTemplate = template.Must(template.New("GetVmGpuAdapters").Parse(` +$vmGpuAdaptersObject = @(Get-VM -Name '{{.VmName}}*' | ?{$_.Name -eq '{{.VmName}}' } | Get-VMGpuPartitionAdapter | %{ @{ + InstancePath=$_.InstancePath; + + CurrentPartitionVRAM=$_.CurrentPartitionVRAM; + MinPartitionVRAM=$_.MinPartitionVRAM; + MaxPartitionVRAM=$_.MaxPartitionVRAM; + OptimalPartitionVRAM=$_.OptimalPartitionVRAM; + + CurrentPartitionEncode=$_.CurrentPartitionEncode; + MinPartitionEncode=$_.MinPartitionEncode; + MaxPartitionEncode=$_.MaxPartitionEncode; + OptimalPartitionEncode=$_.OptimalPartitionEncode; + + CurrentPartitionDecode=$_.CurrentPartitionDecode; + MinPartitionDecode=$_.MinPartitionDecode; + MaxPartitionDecode=$_.MaxPartitionDecode; + OptimalPartitionDecode=$_.OptimalPartitionDecode; + + CurrentPartitionCompute=$_.CurrentPartitionCompute; + MinPartitionCompute=$_.MinPartitionCompute; + MaxPartitionCompute=$_.MaxPartitionCompute; + OptimalPartitionCompute=$_.OptimalPartitionCompute; + +}}) + +if ($vmGpuAdaptersObject) { + # unexpectedly, powershell replaces & wit the unicode representation, so it has to be replaced back + $vmGpuAdapters = (ConvertTo-Json -InputObject $vmGpuAdaptersObject) -replace '\\u0026', '&' + $vmGpuAdapters +} else { + "[]" +} +`)) + +func (c *ClientConfig) GetVmGpuAdapters(ctx context.Context, vmName string) (result []api.VmGpuAdapter, err error) { + result = make([]api.VmGpuAdapter, 0) + + err = c.WinRmClient.RunScriptWithResult(ctx, getVmGpuAdaptersTemplate, getVmGpuAdaptersArgs{ + VmName: vmName, + }, &result) + + return result, err +} + +type updateVmGpuAdapterArgs struct { + VmName string + InstancePath string + VmGpuAdapterJson string +} + +var updateVmGpuAdapterTemplate = template.Must(template.New("UpdateVmGpuAdapter").Parse(` +$ErrorActionPreference = 'Stop' +Import-Module Hyper-V +$vmGpuAdapter = '{{.VmGpuAdapterJson}}' | ConvertFrom-Json + +$vmGpuAdaptersObject = @(Get-VM -Name '{{.VmName}}*' | ?{$_.Name -eq '{{.VmName}}' } | Get-VMGpuPartitionAdapter | ?{$_.InstancePath -eq '{{.InstancePath}}' }) + +if (!$vmGpuAdaptersObject){ + throw "VM gpu adapter does not exist - {{.InstancePath}}" +} + + +$SetVmGpuAdapterArgs = @{ + MinPartitionVRAM=$vmGpuAdapter.MinPartitionVRAM + MaxPartitionVRAM=$vmGpuAdapter.MaxPartitionVRAM + OptimalPartitionVRAM=$vmGpuAdapter.OptimalPartitionVRAM + + MinPartitionEncode=$vmGpuAdapter.MinPartitionEncode + MaxPartitionEncode=$vmGpuAdapter.MaxPartitionEncode + OptimalPartitionEncode=$vmGpuAdapter.OptimalPartitionEncode + + MinPartitionDecode=$vmGpuAdapter.MinPartitionDecode + MaxPartitionDecode=$vmGpuAdapter.MaxPartitionDecode + OptimalPartitionDecode=$vmGpuAdapter.OptimalPartitionDecode + + MinPartitionCompute=$vmGpuAdapter.MinPartitionCompute + MaxPartitionCompute=$vmGpuAdapter.MaxPartitionCompute + OptimalPartitionCompute=$vmGpuAdapter.OptimalPartitionCompute + +} + +@(Get-VM -Name '{{.VmName}}' | Get-VMGpuPartitionAdapter | ?{$_.InstancePath -eq $vmGpuAdapter.InstancePath }) | Set-VMGpuPartitionAdapter @SetVmGpuAdapterArgs +`)) + +func (c *ClientConfig) UpdateVmGpuAdapter( + ctx context.Context, + vmName string, + instancePath string, + minPartitionVram int32, + maxPartitionVram int32, + optimalPartitionVram int32, + minPartitionEncode int64, + maxPartitionEncode int64, + optimalPartitionEncode int64, + minPartitionDecode int32, + maxPartitionDecode int32, + optimalPartitionDecode int32, + minPartitionCompute int32, + maxPartitionCompute int32, + optimalPartitionCompute int32, +) (err error) { + vmGpuAdapterJson, err := json.Marshal(api.VmGpuAdapter{ + VmName: vmName, + InstancePath: instancePath, + MinPartitionVram: minPartitionVram, + MaxPartitionVram: maxPartitionVram, + OptimalPartitionVram: optimalPartitionVram, + MinPartitionEncode: minPartitionEncode, + MaxPartitionEncode: maxPartitionEncode, + OptimalPartitionEncode: optimalPartitionEncode, + MinPartitionDecode: minPartitionDecode, + MaxPartitionDecode: maxPartitionDecode, + OptimalPartitionDecode: optimalPartitionDecode, + MinPartitionCompute: minPartitionCompute, + MaxPartitionCompute: maxPartitionCompute, + OptimalPartitionCompute: optimalPartitionCompute, + }) + + if err != nil { + return err + } + + err = c.WinRmClient.RunFireAndForgetScript(ctx, updateVmGpuAdapterTemplate, updateVmGpuAdapterArgs{ + VmName: vmName, + InstancePath: instancePath, + VmGpuAdapterJson: string(vmGpuAdapterJson), + }) + + return err +} + +type deleteVmGpuAdapterArgs struct { + VmName string + InstancePath string +} + +var deleteVmGpuAdapterTemplate = template.Must(template.New("DeleteVmGpuAdapter").Parse(` +$ErrorActionPreference = 'Stop' + +@(Get-VM -Name '{{.VmName}}*' | ?{$_.Name -eq '{{.VmName}}' } | Get-VMGpuPartitionAdapter | ?{$_.InstancePath -eq '{{.InstancePath}}' }) | Remove-VMGpuPartitionAdapter +`)) + +func (c *ClientConfig) DeleteVmGpuAdapter(ctx context.Context, vmName string, instancePath string) (err error) { + err = c.WinRmClient.RunFireAndForgetScript(ctx, deleteVmGpuAdapterTemplate, deleteVmGpuAdapterArgs{ + VmName: vmName, + InstancePath: instancePath, + }) + + return err +} + +func (c *ClientConfig) CreateOrUpdateVmGpuAdapters(ctx context.Context, vmName string, gpuAdapters []api.VmGpuAdapter) (err error) { + currentGpuAdapters, err := c.GetVmGpuAdapters(ctx, vmName) + if err != nil { + return err + } + + currentGpuAdaptersLength := len(currentGpuAdapters) + desiredGpuAdaptersLength := len(gpuAdapters) + + log.Printf("currentGpuAdaptersLength: %d", currentGpuAdaptersLength) + log.Printf("desiredGpuAdaptersLength: %d", desiredGpuAdaptersLength) + + for i := currentGpuAdaptersLength - 1; i > desiredGpuAdaptersLength-1; i-- { + currentGpuAdapter := currentGpuAdapters[i] + err = c.DeleteVmGpuAdapter(ctx, vmName, currentGpuAdapter.InstancePath) + if err != nil { + return err + } + } + + if currentGpuAdaptersLength > desiredGpuAdaptersLength { + currentGpuAdaptersLength = desiredGpuAdaptersLength + } + + for i := 0; i <= currentGpuAdaptersLength-1; i++ { + currentGpuAdapter := currentGpuAdapters[i] + gpuAdapter := gpuAdapters[i] + + err = c.UpdateVmGpuAdapter( + ctx, + vmName, + currentGpuAdapter.InstancePath, + gpuAdapter.MinPartitionVram, + gpuAdapter.MaxPartitionVram, + gpuAdapter.OptimalPartitionVram, + gpuAdapter.MinPartitionEncode, + gpuAdapter.MaxPartitionEncode, + gpuAdapter.OptimalPartitionEncode, + gpuAdapter.MinPartitionDecode, + gpuAdapter.MaxPartitionDecode, + gpuAdapter.OptimalPartitionDecode, + gpuAdapter.MinPartitionCompute, + gpuAdapter.MaxPartitionCompute, + gpuAdapter.OptimalPartitionCompute, + ) + if err != nil { + return err + } + } + + for i := currentGpuAdaptersLength - 1 + 1; i <= desiredGpuAdaptersLength-1; i++ { + gpuAdapter := gpuAdapters[i] + err = c.CreateVmGpuAdapter( + ctx, + vmName, + gpuAdapter.InstancePath, + gpuAdapter.MinPartitionVram, + gpuAdapter.MaxPartitionVram, + gpuAdapter.OptimalPartitionVram, + gpuAdapter.MinPartitionEncode, + gpuAdapter.MaxPartitionEncode, + gpuAdapter.OptimalPartitionEncode, + gpuAdapter.MinPartitionDecode, + gpuAdapter.MaxPartitionDecode, + gpuAdapter.OptimalPartitionDecode, + gpuAdapter.MinPartitionCompute, + gpuAdapter.MaxPartitionCompute, + gpuAdapter.OptimalPartitionCompute, + ) + + if err != nil { + return err + } + } + + return nil +} diff --git a/api/provider.go b/api/provider.go index af87ebb..e7c9b5f 100644 --- a/api/provider.go +++ b/api/provider.go @@ -9,6 +9,7 @@ type Client interface { HypervVmIntegrationServiceClient HypervVmNetworkAdapterClient HypervVmProcessorClient + HypervGpuAdapterClient HypervVmStatusClient HypervVmSwitchClient HypervIsoImageClient diff --git a/api/vm_gpu_adapter.go b/api/vm_gpu_adapter.go new file mode 100644 index 0000000..8455c31 --- /dev/null +++ b/api/vm_gpu_adapter.go @@ -0,0 +1,141 @@ +package api + +import ( + "context" + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ExpandGpuAdapters(d *schema.ResourceData) ([]VmGpuAdapter, error) { + expandedGpuAdapters := make([]VmGpuAdapter, 0) + + if v, ok := d.GetOk("gpu_adapters"); ok { + gpuAdapters := v.([]interface{}) + for _, gpuAdapter := range gpuAdapters { + gpuAdapter, ok := gpuAdapter.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("[ERROR][hyperv] gpu_adapters should be a Hash - was '%+v'", gpuAdapter) + } + + log.Printf("[DEBUG] gpuAdapter = [%+v]", gpuAdapter) + + min_partition_encode, _ := strconv.ParseInt(gpuAdapter["min_partition_encode"].(string), 10, 64) + max_partition_encode, _ := strconv.ParseInt(gpuAdapter["max_partition_encode"].(string), 10, 64) + optimal_partition_encode, _ := strconv.ParseInt(gpuAdapter["optimal_partition_encode"].(string), 10, 64) + + expandedGpuAdapter := VmGpuAdapter{ + InstancePath: gpuAdapter["device_path_name"].(string), + MinPartitionVram: int32(gpuAdapter["min_partition_vram"].(int)), + MaxPartitionVram: int32(gpuAdapter["max_partition_vram"].(int)), + OptimalPartitionVram: int32(gpuAdapter["optimal_partition_vram"].(int)), + MinPartitionEncode: int64(min_partition_encode), + MaxPartitionEncode: int64(max_partition_encode), + OptimalPartitionEncode: int64(optimal_partition_encode), + MinPartitionDecode: int32(gpuAdapter["min_partition_decode"].(int)), + MaxPartitionDecode: int32(gpuAdapter["max_partition_decode"].(int)), + OptimalPartitionDecode: int32(gpuAdapter["optimal_partition_decode"].(int)), + MinPartitionCompute: int32(gpuAdapter["min_partition_compute"].(int)), + MaxPartitionCompute: int32(gpuAdapter["max_partition_compute"].(int)), + OptimalPartitionCompute: int32(gpuAdapter["optimal_partition_compute"].(int)), + } + + expandedGpuAdapters = append(expandedGpuAdapters, expandedGpuAdapter) + } + } + + return expandedGpuAdapters, nil +} + +func FlattenGpuAdapters(gpuAdapters *[]VmGpuAdapter) []interface{} { + if gpuAdapters == nil || len(*gpuAdapters) < 1 { + return nil + } + + flattenedGpuAdapters := make([]interface{}, 0) + + for _, gpuAdapter := range *gpuAdapters { + flattenedGpuAdapter := make(map[string]interface{}) + + min_partition_encode := strconv.Itoa(int(gpuAdapter.MinPartitionEncode)) + max_partition_encode := strconv.Itoa(int(gpuAdapter.MaxPartitionEncode)) + optimal_partition_encode := strconv.Itoa(int(gpuAdapter.OptimalPartitionEncode)) + + flattenedGpuAdapter["device_path_name"] = gpuAdapter.InstancePath + flattenedGpuAdapter["min_partition_vram"] = gpuAdapter.MinPartitionVram + flattenedGpuAdapter["max_partition_vram"] = gpuAdapter.MaxPartitionVram + flattenedGpuAdapter["optimal_partition_vram"] = gpuAdapter.OptimalPartitionVram + flattenedGpuAdapter["min_partition_encode"] = min_partition_encode + flattenedGpuAdapter["max_partition_encode"] = max_partition_encode + flattenedGpuAdapter["optimal_partition_encode"] = optimal_partition_encode + flattenedGpuAdapter["min_partition_decode"] = gpuAdapter.MinPartitionDecode + flattenedGpuAdapter["max_partition_decode"] = gpuAdapter.MaxPartitionDecode + flattenedGpuAdapter["optimal_partition_decode"] = gpuAdapter.OptimalPartitionDecode + flattenedGpuAdapter["min_partition_compute"] = gpuAdapter.MinPartitionCompute + flattenedGpuAdapter["max_partition_compute"] = gpuAdapter.MaxPartitionCompute + flattenedGpuAdapter["optimal_partition_compute"] = gpuAdapter.OptimalPartitionCompute + + flattenedGpuAdapters = append(flattenedGpuAdapters, flattenedGpuAdapter) + } + + return flattenedGpuAdapters +} + +type VmGpuAdapter struct { + VmName string + InstancePath string + MinPartitionVram int32 + MaxPartitionVram int32 + OptimalPartitionVram int32 + MinPartitionEncode int64 + MaxPartitionEncode int64 + OptimalPartitionEncode int64 + MinPartitionDecode int32 + MaxPartitionDecode int32 + OptimalPartitionDecode int32 + MinPartitionCompute int32 + MaxPartitionCompute int32 + OptimalPartitionCompute int32 +} + +type HypervGpuAdapterClient interface { + CreateVmGpuAdapter( + ctx context.Context, + vmName string, + devicePathName string, + minPartitionVram int32, + maxPartitionVram int32, + optimalPartitionVram int32, + minPartitionEncode int64, + maxPartitionEncode int64, + optimalPartitionEncode int64, + minPartitionDecode int32, + maxPartitionDecode int32, + optimalPartitionDecode int32, + minPartitionCompute int32, + maxPartitionCompute int32, + optimalPartitionCompute int32, + ) (err error) + GetVmGpuAdapters(ctx context.Context, vmName string) (result []VmGpuAdapter, err error) + UpdateVmGpuAdapter( + ctx context.Context, + vmName string, + instancePath string, + minPartitionVram int32, + maxPartitionVram int32, + optimalPartitionVram int32, + minPartitionEncode int64, + maxPartitionEncode int64, + optimalPartitionEncode int64, + minPartitionDecode int32, + maxPartitionDecode int32, + optimalPartitionDecode int32, + minPartitionCompute int32, + maxPartitionCompute int32, + optimalPartitionCompute int32, + ) (err error) + DeleteVmGpuAdapter(ctx context.Context, vmName string, instancePath string) (err error) + CreateOrUpdateVmGpuAdapters(ctx context.Context, vmName string, gpuAdapters []VmGpuAdapter) (err error) +} diff --git a/internal/provider/data_source_hyperv_machine_instance.go b/internal/provider/data_source_hyperv_machine_instance.go index afa9790..f686e1c 100644 --- a/internal/provider/data_source_hyperv_machine_instance.go +++ b/internal/provider/data_source_hyperv_machine_instance.go @@ -246,7 +246,101 @@ func dataSourceHyperVMachineInstance() *schema.Resource { Default: 5, Description: "The amount of time in seconds to wait between trying to get ip addresses for network cards on the virtual machine.", }, - + "gpu_adapters": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "device_path_name": { + Type: schema.TypeString, + Required: true, + Description: "The device path name of the GPU adapter.", + }, + "min_partition_vram": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the minimum amount of VRAM to dedicate to the virtual machine.", + }, + "max_partition_vram": { + Type: schema.TypeInt, + Optional: true, + Default: 1000000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the maximum amount of VRAM to dedicate to the virtual machine.", + }, + "optimal_partition_vram": { + Type: schema.TypeInt, + Optional: true, + Default: 50000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the optimal amount of VRAM to dedicate to the virtual machine.", + }, + "min_partition_encode": { + Type: schema.TypeString, + Optional: true, + Default: "0", + Description: "Specifies the minimum number of encode streams to dedicate to the virtual machine.", + }, + "max_partition_encode": { + Type: schema.TypeString, + Optional: true, + Default: "18446744073709551615", + Description: "Specifies the maximum number of encode streams to dedicate to the virtual machine.", + }, + "optimal_partition_encode": { + Type: schema.TypeString, + Optional: true, + Default: "9223372036854775807", + Description: "Specifies the optimal number of encode streams to dedicate to the virtual machine.", + }, + "min_partition_decode": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the minimum number of decode streams to dedicate to the virtual machine.", + }, + "max_partition_decode": { + Type: schema.TypeInt, + Optional: true, + Default: 1000000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the maximum number of decode streams to dedicate to the virtual machine.", + }, + "optimal_partition_decode": { + Type: schema.TypeInt, + Optional: true, + Default: 50000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the optimal number of decode streams to dedicate to the virtual machine.", + }, + "min_partition_compute": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the minimum number of compute units to dedicate to the virtual machine.", + }, + "max_partition_compute": { + Type: schema.TypeInt, + Optional: true, + Default: 1000000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the maximum number of compute units to dedicate to the virtual machine.", + }, + "optimal_partition_compute": { + Type: schema.TypeInt, + Optional: true, + Default: 50000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the optimal number of compute units to dedicate to the virtual machine.", + }, + }, + }, + Description: "", + }, "vm_processor": { Type: schema.TypeList, Optional: true, @@ -939,6 +1033,10 @@ func datasourceHyperVMachineInstanceRead(ctx context.Context, d *schema.Resource if err != nil { return diag.FromErr(err) } + gpuAdapters, err := client.GetVmGpuAdapters(ctx, name) + if err != nil { + return diag.FromErr(err) + } vmState, err := client.GetVmStatus(ctx, name) if err != nil { @@ -1020,6 +1118,14 @@ func datasourceHyperVMachineInstanceRead(ctx context.Context, d *schema.Resource log.Printf("[INFO][hyperv][read] networkAdapters: %v", networkAdapters) log.Printf("[INFO][hyperv][read] flattenedNetworkAdapters: %v", flattenedNetworkAdapters) + flattenedGpuAdapters := api.FlattenGpuAdapters(&gpuAdapters) + if err := d.Set("gpu_adapters", flattenedGpuAdapters); err != nil { + return diag.Errorf("[DEBUG] Error setting gpu_adapters error: %v", err) + } + log.Printf("[INFO][hyperv][read] gpuAdapters: %v", gpuAdapters) + log.Printf("[INFO][hyperv][read] flattenedGpuAdapters: %v", flattenedGpuAdapters) + + if err := d.Set("name", vm.Name); err != nil { return diag.FromErr(err) } diff --git a/internal/provider/resource_hyperv_machine_instance.go b/internal/provider/resource_hyperv_machine_instance.go index a56a27a..437d7eb 100644 --- a/internal/provider/resource_hyperv_machine_instance.go +++ b/internal/provider/resource_hyperv_machine_instance.go @@ -271,6 +271,102 @@ func resourceHyperVMachineInstance() *schema.Resource { Description: "The amount of time in seconds to wait between trying to get ip addresses for network cards on the virtual machine.", }, + "gpu_adapters": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "device_path_name": { + Type: schema.TypeString, + Required: true, + Description: "The device path name of the GPU adapter.", + }, + "min_partition_vram": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the minimum amount of VRAM to dedicate to the virtual machine.", + }, + "max_partition_vram": { + Type: schema.TypeInt, + Optional: true, + Default: 1000000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the maximum amount of VRAM to dedicate to the virtual machine.", + }, + "optimal_partition_vram": { + Type: schema.TypeInt, + Optional: true, + Default: 50000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the optimal amount of VRAM to dedicate to the virtual machine.", + }, + "min_partition_encode": { + Type: schema.TypeString, + Optional: true, + Default: "0", + Description: "Specifies the minimum number of encode streams to dedicate to the virtual machine.", + }, + "max_partition_encode": { + Type: schema.TypeString, + Optional: true, + Default: "18446744073709551615", + Description: "Specifies the maximum number of encode streams to dedicate to the virtual machine.", + }, + "optimal_partition_encode": { + Type: schema.TypeString, + Optional: true, + Default: "9223372036854775807", + Description: "Specifies the optimal number of encode streams to dedicate to the virtual machine.", + }, + "min_partition_decode": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the minimum number of decode streams to dedicate to the virtual machine.", + }, + "max_partition_decode": { + Type: schema.TypeInt, + Optional: true, + Default: 1000000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the maximum number of decode streams to dedicate to the virtual machine.", + }, + "optimal_partition_decode": { + Type: schema.TypeInt, + Optional: true, + Default: 50000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the optimal number of decode streams to dedicate to the virtual machine.", + }, + "min_partition_compute": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the minimum number of compute units to dedicate to the virtual machine.", + }, + "max_partition_compute": { + Type: schema.TypeInt, + Optional: true, + Default: 1000000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the maximum number of compute units to dedicate to the virtual machine.", + }, + "optimal_partition_compute": { + Type: schema.TypeInt, + Optional: true, + Default: 50000000, + ValidateDiagFunc: IntBetween(0, 1000000000), + Description: "Specifies the optimal number of compute units to dedicate to the virtual machine.", + }, + }, + }, + Description: "", + }, + "vm_processor": { Type: schema.TypeList, Optional: true, @@ -983,6 +1079,11 @@ func resourceHyperVMachineInstanceCreate(ctx context.Context, d *schema.Resource return diag.Errorf("[ERROR][hyperv][create] Either dynamic or static must be selected") } + gpuAdapters, err := api.ExpandGpuAdapters(d) + if err != nil { + return diag.FromErr(err) + } + vmProcessors, err := api.ExpandVmProcessors(d) if err != nil { return diag.FromErr(err) @@ -1026,6 +1127,11 @@ func resourceHyperVMachineInstanceCreate(ctx context.Context, d *schema.Resource return diag.FromErr(err) } + err = client.CreateOrUpdateVmGpuAdapters(ctx, name, gpuAdapters) + if err != nil { + return diag.FromErr(err) + } + err = client.CreateOrUpdateVmProcessors(ctx, name, vmProcessors) if err != nil { return diag.FromErr(err) @@ -1108,6 +1214,10 @@ func resourceHyperVMachineInstanceRead(ctx context.Context, d *schema.ResourceDa if err != nil { return diag.FromErr(err) } + gpuAdapters, err := client.GetVmGpuAdapters(ctx, name) + if err != nil { + return diag.FromErr(err) + } vmFirmwares := client.GetNoVmFirmwares(ctx) if vm.Generation > 1 { @@ -1185,6 +1295,13 @@ func resourceHyperVMachineInstanceRead(ctx context.Context, d *schema.ResourceDa log.Printf("[INFO][hyperv][read] networkAdapters: %v", networkAdapters) log.Printf("[INFO][hyperv][read] flattenedNetworkAdapters: %v", flattenedNetworkAdapters) + flattenedGpuAdapters := api.FlattenGpuAdapters(&gpuAdapters) + if err := d.Set("gpu_adapters", flattenedGpuAdapters); err != nil { + return diag.Errorf("[DEBUG] Error setting gpu_adapters error: %v", err) + } + log.Printf("[INFO][hyperv][read] gpuAdapters: %v", gpuAdapters) + log.Printf("[INFO][hyperv][read] flattenedGpuAdapters: %v", flattenedGpuAdapters) + flattenedVmFirmwares := api.FlattenVmFirmwares(&vmFirmwares) if err := d.Set("vm_firmware", flattenedVmFirmwares); err != nil { return diag.Errorf("[DEBUG] Error setting vm_firmware error: %v", err) @@ -1303,6 +1420,7 @@ func resourceHyperVMachineInstanceUpdate(ctx context.Context, d *schema.Resource d.HasChange("vm_processor") || d.HasChange("integration_services") || d.HasChange("network_adaptors") || + d.HasChange("gpu_adapters") || d.HasChange("dvd_drives") || d.HasChange("hard_disk_drives") @@ -1366,6 +1484,18 @@ func resourceHyperVMachineInstanceUpdate(ctx context.Context, d *schema.Resource } } + if d.HasChange("gpu_adapters") { + gpuAdapters, err := api.ExpandGpuAdapters(d) + if err != nil { + return diag.FromErr(err) + } + + err = client.CreateOrUpdateVmGpuAdapters(ctx, name, gpuAdapters) + if err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("vm_processor") { vmProcessors, err := api.ExpandVmProcessors(d) if err != nil {