0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-08-23 20:08:34 +00:00

misc(vm2): add support for vga (#1328)

* misc(vm2): add support for `vga`
* fix: use random VM IDs in parallel acc tests

---------

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
Pavel Boldyrev 2024-05-27 21:30:12 -04:00 committed by GitHub
parent e0097d9880
commit d843e46b37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 564 additions and 86 deletions

View File

@ -31,6 +31,7 @@ This is an experimental implementation of a Proxmox VM datasource using Plugin F
- `tags` (Set of String) The tags assigned to the VM. - `tags` (Set of String) The tags assigned to the VM.
- `template` (Boolean) Whether the VM is a template. - `template` (Boolean) Whether the VM is a template.
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
- `vga` (Attributes) The VGA configuration. (see [below for nested schema](#nestedatt--vga))
<a id="nestedatt--clone"></a> <a id="nestedatt--clone"></a>
### Nested Schema for `clone` ### Nested Schema for `clone`
@ -67,3 +68,13 @@ Optional:
Optional: Optional:
- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled. - `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled.
<a id="nestedatt--vga"></a>
### Nested Schema for `vga`
Optional:
- `clipboard` (String) Enable a specific clipboard.
- `memory` (Number) The VGA memory in megabytes (4-512 MB). Has no effect with serial display.
- `type` (String) The VGA type.

View File

@ -541,9 +541,9 @@ output "ubuntu_vm_public_key" {
- `serial3` - Serial Terminal 3. - `serial3` - Serial Terminal 3.
- `std` - Standard VGA. - `std` - Standard VGA.
- `virtio` - VirtIO-GPU. - `virtio` - VirtIO-GPU.
- `virtio-gl` - VirtIO-GPU with 3D acceleration (VirGL). VirGL support needs some extra libraries that arent installed by default. See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_virtual_machines_settings) section 10.2.8.for more information. - `virtio-gl` - VirtIO-GPU with 3D acceleration (VirGL). VirGL support needs some extra libraries that arent installed by default. See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_virtual_machines_settings) section 10.2.8 for more information.
- `vmware` - VMware Compatible. - `vmware` - VMware Compatible.
- `clipboard` - (Optional) Enable VNC clipboard by setting to `vnc`. See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_virtual_machines_settings) section 10.2.8.for more information. - `clipboard` - (Optional) Enable VNC clipboard by setting to `vnc`. See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_virtual_machines_settings) section 10.2.8 for more information.
- `vm_id` - (Optional) The VM identifier. - `vm_id` - (Optional) The VM identifier.
- `hook_script_file_id` - (Optional) The identifier for a file containing a hook script (needs to be executable). - `hook_script_file_id` - (Optional) The identifier for a file containing a hook script (needs to be executable).

View File

@ -38,6 +38,7 @@ The attributes are also marked as optional to allow the practitioner to set (or
- `tags` (Set of String) The tags assigned to the VM. - `tags` (Set of String) The tags assigned to the VM.
- `template` (Boolean) Set to true to create a VM template. - `template` (Boolean) Set to true to create a VM template.
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
- `vga` (Attributes) Configure the VGA Hardware. If you want to use high resolution modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU 2.9 the default VGA display type is `std` for all OS types besides some Windows versions (XP and older) which use `cirrus`. The `qxl` option enables the SPICE display server. For win* OS you can select how many independent displays you want, Linux guests can add displays themself. You can also run without any graphic card, using a serial device as terminal. See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_virtual_machines_settings) section 10.2.8 for more information and available configuration parameters. (see [below for nested schema](#nestedatt--vga))
<a id="nestedatt--clone"></a> <a id="nestedatt--clone"></a>
### Nested Schema for `clone` ### Nested Schema for `clone`
@ -77,3 +78,13 @@ Optional:
- `delete` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Setting a timeout for a Delete operation is only applicable if changes are saved into state before the destroy operation occurs. - `delete` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Setting a timeout for a Delete operation is only applicable if changes are saved into state before the destroy operation occurs.
- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled. - `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled.
- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). - `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
<a id="nestedatt--vga"></a>
### Nested Schema for `vga`
Optional:
- `clipboard` (String) Enable a specific clipboard. If not set, depending on the display type the SPICE one will be added. Currently only `vnc` is available. Migration with VNC clipboard is not supported by Proxmox.
- `memory` (Number) The VGA memory in megabytes (4-512 MB). Has no effect with serial display.
- `type` (String) The VGA type (defaults to `std`).

View File

@ -18,7 +18,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr" "github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr"
proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types" proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types"
@ -54,7 +54,7 @@ func (r *aclResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *
stringplanmodifier.RequiresReplace(), stringplanmodifier.RequiresReplace(),
}, },
}, },
"id": structure.IDAttribute(), "id": attribute.ID(),
"path": schema.StringAttribute{ "path": schema.StringAttribute{
Description: "Access control path", Description: "Access control path",
Required: true, Required: true,

View File

@ -16,7 +16,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/fwprovider/validators" "github.com/bpg/terraform-provider-proxmox/fwprovider/validators"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/access" "github.com/bpg/terraform-provider-proxmox/proxmox/access"
@ -69,7 +69,7 @@ func (r *userTokenResource) Schema(
}, "must be a valid RFC3339 date"), }, "must be a valid RFC3339 date"),
}, },
}, },
"id": structure.IDAttribute("Unique token identifier with format `<user_id>!<token_name>`."), "id": attribute.ID("Unique token identifier with format `<user_id>!<token_name>`."),
"privileges_separation": schema.BoolAttribute{ "privileges_separation": schema.BoolAttribute{
Description: "Restrict API token privileges with separate ACLs (default)", Description: "Restrict API token privileges with separate ACLs (default)",
MarkdownDescription: "Restrict API token privileges with separate ACLs (default), " + MarkdownDescription: "Restrict API token privileges with separate ACLs (default), " +

View File

@ -4,17 +4,18 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
package structure package attribute
import ( import (
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
) )
// IDAttribute generates an attribute definition suitable for the always-present `id` attribute. // ID generates an attribute definition suitable for the always-present `id` attribute.
func IDAttribute(desc ...string) schema.StringAttribute { func ID(desc ...string) schema.StringAttribute {
attr := schema.StringAttribute{ a := schema.StringAttribute{
Computed: true, Computed: true,
Description: "The unique identifier of this resource.", Description: "The unique identifier of this resource.",
PlanModifiers: []planmodifier.String{ PlanModifiers: []planmodifier.String{
@ -23,8 +24,18 @@ func IDAttribute(desc ...string) schema.StringAttribute {
} }
if len(desc) > 0 { if len(desc) > 0 {
attr.Description = desc[0] a.Description = desc[0]
} }
return attr return a
}
// ShouldBeRemoved evaluates if an attribute should be removed from the plan during update.
func ShouldBeRemoved(plan attr.Value, state attr.Value, isClone bool) bool {
return !IsDefined(plan) && IsDefined(state) && !isClone
}
// IsDefined returns true if attribute is known and not null.
func IsDefined(v attr.Value) bool {
return !v.IsNull() && !v.IsUnknown()
} }

View File

@ -14,7 +14,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
hagroups "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/groups" hagroups "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/groups"
@ -51,7 +51,7 @@ func (d *haGroupDatasource) Schema(_ context.Context, _ datasource.SchemaRequest
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
Description: "Retrieves information about a specific High Availability group.", Description: "Retrieves information about a specific High Availability group.",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": structure.IDAttribute(), "id": attribute.ID(),
"group": schema.StringAttribute{ "group": schema.StringAttribute{
Description: "The identifier of the High Availability group to read.", Description: "The identifier of the High Availability group to read.",
Required: true, Required: true,

View File

@ -15,7 +15,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
hagroups "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/groups" hagroups "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/groups"
@ -57,7 +57,7 @@ func (d *haGroupsDatasource) Schema(_ context.Context, _ datasource.SchemaReques
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
Description: "Retrieves the list of High Availability groups.", Description: "Retrieves the list of High Availability groups.",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": structure.IDAttribute(), "id": attribute.ID(),
"group_ids": schema.SetAttribute{ "group_ids": schema.SetAttribute{
Description: "The identifiers of the High Availability groups.", Description: "The identifiers of the High Availability groups.",
ElementType: types.StringType, ElementType: types.StringType,

View File

@ -14,7 +14,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
haresources "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/resources" haresources "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/resources"
proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types" proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types"
@ -50,7 +50,7 @@ func (d *haResourceDatasource) Schema(_ context.Context, _ datasource.SchemaRequ
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
Description: "Retrieves the list of High Availability resources.", Description: "Retrieves the list of High Availability resources.",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": structure.IDAttribute(), "id": attribute.ID(),
"resource_id": schema.StringAttribute{ "resource_id": schema.StringAttribute{
Description: "The identifier of the Proxmox HA resource to read.", Description: "The identifier of the Proxmox HA resource to read.",
Required: true, Required: true,

View File

@ -17,7 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
haresources "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/resources" haresources "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/resources"
@ -64,7 +64,7 @@ func (d *haResourcesDatasource) Schema(_ context.Context, _ datasource.SchemaReq
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
Description: "Retrieves the list of High Availability resources.", Description: "Retrieves the list of High Availability resources.",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": structure.IDAttribute(), "id": attribute.ID(),
"type": schema.StringAttribute{ "type": schema.StringAttribute{
Description: "The type of High Availability resources to fetch (`vm` or `ct`). All resources " + Description: "The type of High Availability resources to fetch (`vm` or `ct`). All resources " +
"will be fetched if this option is unset.", "will be fetched if this option is unset.",

View File

@ -23,7 +23,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
hagroups "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/groups" hagroups "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/groups"
@ -64,7 +64,7 @@ func (r *hagroupResource) Schema(
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
Description: "Manages a High Availability group in a Proxmox VE cluster.", Description: "Manages a High Availability group in a Proxmox VE cluster.",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": structure.IDAttribute(), "id": attribute.ID(),
"group": schema.StringAttribute{ "group": schema.StringAttribute{
Description: "The identifier of the High Availability group to manage.", Description: "The identifier of the High Availability group to manage.",
Required: true, Required: true,

View File

@ -12,7 +12,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
haresources "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/resources" haresources "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/ha/resources"
proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types" proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types"
@ -68,7 +68,7 @@ func (r *haResourceResource) Schema(
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
Description: "Manages Proxmox HA resources.", Description: "Manages Proxmox HA resources.",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": structure.IDAttribute(), "id": attribute.ID(),
"resource_id": schema.StringAttribute{ "resource_id": schema.StringAttribute{
Description: "The Proxmox HA resource identifier", Description: "The Proxmox HA resource identifier",
Required: true, Required: true,

View File

@ -19,7 +19,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster/mapping" "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/mapping"
proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types/hardwaremapping" proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types/hardwaremapping"
@ -179,7 +179,7 @@ func (d *dataSource) Schema(
Computed: true, Computed: true,
Description: "The identifiers of the hardware mappings.", Description: "The identifiers of the hardware mappings.",
}, },
schemaAttrNameTerraformID: structure.IDAttribute( schemaAttrNameTerraformID: attribute.ID(
"The unique identifier of this hardware mappings data source.", "The unique identifier of this hardware mappings data source.",
), ),
schemaAttrNameType: schema.StringAttribute{ schemaAttrNameType: schema.StringAttribute{

View File

@ -15,7 +15,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types/hardwaremapping" customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types/hardwaremapping"
"github.com/bpg/terraform-provider-proxmox/fwprovider/validators" "github.com/bpg/terraform-provider-proxmox/fwprovider/validators"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
@ -155,7 +155,7 @@ func (d *dataSourcePCI) Schema(_ context.Context, _ datasource.SchemaRequest, re
Description: "The name of this PCI hardware mapping.", Description: "The name of this PCI hardware mapping.",
Required: true, Required: true,
}, },
schemaAttrNameTerraformID: structure.IDAttribute( schemaAttrNameTerraformID: attribute.ID(
"The unique identifier of this PCI hardware mapping data source.", "The unique identifier of this PCI hardware mapping data source.",
), ),
}, },

View File

@ -15,7 +15,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types/hardwaremapping" customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types/hardwaremapping"
"github.com/bpg/terraform-provider-proxmox/fwprovider/validators" "github.com/bpg/terraform-provider-proxmox/fwprovider/validators"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
@ -137,7 +137,7 @@ func (d *datasourceUSB) Schema(_ context.Context, _ datasource.SchemaRequest, re
Description: "The name of this USB hardware mapping.", Description: "The name of this USB hardware mapping.",
Required: true, Required: true,
}, },
schemaAttrNameTerraformID: structure.IDAttribute( schemaAttrNameTerraformID: attribute.ID(
"The unique identifier of this USB hardware mapping data source.", "The unique identifier of this USB hardware mapping data source.",
), ),
}, },

View File

@ -22,7 +22,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types/hardwaremapping" customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types/hardwaremapping"
"github.com/bpg/terraform-provider-proxmox/fwprovider/validators" "github.com/bpg/terraform-provider-proxmox/fwprovider/validators"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
@ -264,7 +264,7 @@ func (r *resourcePCI) Schema(_ context.Context, _ resource.SchemaRequest, resp *
Description: "The name of this PCI hardware mapping.", Description: "The name of this PCI hardware mapping.",
Required: true, Required: true,
}, },
schemaAttrNameTerraformID: structure.IDAttribute( schemaAttrNameTerraformID: attribute.ID(
"The unique identifier of this PCI hardware mapping resource.", "The unique identifier of this PCI hardware mapping resource.",
), ),
}, },

View File

@ -21,7 +21,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types/hardwaremapping" customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types/hardwaremapping"
"github.com/bpg/terraform-provider-proxmox/fwprovider/validators" "github.com/bpg/terraform-provider-proxmox/fwprovider/validators"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
@ -249,7 +249,7 @@ func (r *resourceUSB) Schema(_ context.Context, _ resource.SchemaRequest, resp *
Description: "The name of this hardware mapping.", Description: "The name of this hardware mapping.",
Required: true, Required: true,
}, },
schemaAttrNameTerraformID: structure.IDAttribute( schemaAttrNameTerraformID: attribute.ID(
"The unique identifier of this USB hardware mapping resource.", "The unique identifier of this USB hardware mapping resource.",
), ),
}, },

View File

@ -24,7 +24,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types" customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
@ -173,7 +173,7 @@ func (r *linuxBridgeResource) Schema(
Description: "Manages a Linux Bridge network interface in a Proxmox VE node.", Description: "Manages a Linux Bridge network interface in a Proxmox VE node.",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
// Base attributes // Base attributes
"id": structure.IDAttribute("A unique identifier with format `<node name>:<iface>`"), "id": attribute.ID("A unique identifier with format `<node name>:<iface>`"),
"node_name": schema.StringAttribute{ "node_name": schema.StringAttribute{
Description: "The name of the node.", Description: "The name of the node.",
Required: true, Required: true,

View File

@ -22,7 +22,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types" customtypes "github.com/bpg/terraform-provider-proxmox/fwprovider/types"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
@ -146,7 +146,7 @@ func (r *linuxVLANResource) Schema(
Description: "Manages a Linux VLAN network interface in a Proxmox VE node.", Description: "Manages a Linux VLAN network interface in a Proxmox VE node.",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
// Base attributes // Base attributes
"id": structure.IDAttribute("A unique identifier with format `<node name>:<iface>`."), "id": attribute.ID("A unique identifier with format `<node name>:<iface>`."),
"node_name": schema.StringAttribute{ "node_name": schema.StringAttribute{
Description: "The name of the node.", Description: "The name of the node.",
Required: true, Required: true,

View File

@ -27,7 +27,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox/api" "github.com/bpg/terraform-provider-proxmox/proxmox/api"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
@ -193,7 +193,7 @@ func (r *downloadFileResource) Schema(
"It can be fully compatible and faster replacement for image files created using " + "It can be fully compatible and faster replacement for image files created using " +
"`proxmox_virtual_environment_file`. Supports images for VMs (ISO images) and LXC (CT Templates).", "`proxmox_virtual_environment_file`. Supports images for VMs (ISO images) and LXC (CT Templates).",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": structure.IDAttribute(), "id": attribute.ID(),
"content_type": schema.StringAttribute{ "content_type": schema.StringAttribute{
Description: "The file content type. Must be `iso` for VM images or `vztmpl` for LXC images.", Description: "The file content type. Must be `iso` for VM images or `vztmpl` for LXC images.",
Required: true, Required: true,

View File

@ -21,7 +21,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure" "github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/fwprovider/validators" "github.com/bpg/terraform-provider-proxmox/fwprovider/validators"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster" "github.com/bpg/terraform-provider-proxmox/proxmox/cluster"
@ -456,7 +456,7 @@ func (r *clusterOptionsResource) Schema(
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
Description: "Manages Proxmox VE Cluster Datacenter options.", Description: "Manages Proxmox VE Cluster Datacenter options.",
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": structure.IDAttribute(), "id": attribute.ID(),
"email_from": schema.StringAttribute{ "email_from": schema.StringAttribute{
Description: "email address to send notification from (default is root@$hostname).", Description: "email address to send notification from (default is root@$hostname).",
Optional: true, Optional: true,

View File

@ -9,6 +9,7 @@ import (
"testing" "testing"
"text/template" "text/template"
"github.com/brianvoe/gofakeit/v7"
"github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov5" "github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tfprotov6"
@ -105,6 +106,10 @@ func (e *Environment) RenderConfig(cfg string) string {
tmpl, err := template.New("config").Parse("{{.ProviderConfig}}" + cfg) tmpl, err := template.New("config").Parse("{{.ProviderConfig}}" + cfg)
require.NoError(e.t, err) require.NoError(e.t, err)
e.templateVars["RandomVMID"] = gofakeit.IntRange(100_000, 1_000_000)
e.templateVars["RandomVMID1"] = gofakeit.IntRange(100_000, 1_000_000)
e.templateVars["RandomVMID2"] = gofakeit.IntRange(100_000, 1_000_000)
var buf bytes.Buffer var buf bytes.Buffer
err = tmpl.Execute(&buf, e.templateVars) err = tmpl.Execute(&buf, e.templateVars)
require.NoError(e.t, err) require.NoError(e.t, err)

View File

@ -4,11 +4,11 @@ import (
"context" "context"
"reflect" "reflect"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types" proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types"
) )
@ -153,65 +153,65 @@ func FillUpdateBody(
} }
if !plan.Affinity.Equal(state.Affinity) { if !plan.Affinity.Equal(state.Affinity) {
if shouldBeRemoved(plan.Affinity, state.Affinity, isClone) { if attribute.ShouldBeRemoved(plan.Affinity, state.Affinity, isClone) {
del("CPUAffinity") del("CPUAffinity")
} else if isDefined(plan.Affinity) { } else if attribute.IsDefined(plan.Affinity) {
updateBody.CPUAffinity = plan.Affinity.ValueStringPointer() updateBody.CPUAffinity = plan.Affinity.ValueStringPointer()
} }
} }
if !plan.Architecture.Equal(state.Architecture) { if !plan.Architecture.Equal(state.Architecture) {
if shouldBeRemoved(plan.Architecture, state.Architecture, isClone) { if attribute.ShouldBeRemoved(plan.Architecture, state.Architecture, isClone) {
del("CPUArchitecture") del("CPUArchitecture")
} else if isDefined(plan.Architecture) { } else if attribute.IsDefined(plan.Architecture) {
updateBody.CPUArchitecture = plan.Architecture.ValueStringPointer() updateBody.CPUArchitecture = plan.Architecture.ValueStringPointer()
} }
} }
if !plan.Cores.Equal(state.Cores) { if !plan.Cores.Equal(state.Cores) {
if shouldBeRemoved(plan.Cores, state.Cores, isClone) { if attribute.ShouldBeRemoved(plan.Cores, state.Cores, isClone) {
del("CPUCores") del("CPUCores")
} else if isDefined(plan.Cores) { } else if attribute.IsDefined(plan.Cores) {
updateBody.CPUCores = plan.Cores.ValueInt64Pointer() updateBody.CPUCores = plan.Cores.ValueInt64Pointer()
} }
} }
if !plan.Limit.Equal(state.Limit) { if !plan.Limit.Equal(state.Limit) {
if shouldBeRemoved(plan.Limit, state.Limit, isClone) { if attribute.ShouldBeRemoved(plan.Limit, state.Limit, isClone) {
del("CPULimit") del("CPULimit")
} else if isDefined(plan.Sockets) { } else if attribute.IsDefined(plan.Sockets) {
updateBody.CPULimit = plan.Limit.ValueInt64Pointer() updateBody.CPULimit = plan.Limit.ValueInt64Pointer()
} }
} }
if !plan.Sockets.Equal(state.Sockets) { if !plan.Sockets.Equal(state.Sockets) {
if shouldBeRemoved(plan.Sockets, state.Sockets, isClone) { if attribute.ShouldBeRemoved(plan.Sockets, state.Sockets, isClone) {
del("CPUSockets") del("CPUSockets")
} else if isDefined(plan.Sockets) { } else if attribute.IsDefined(plan.Sockets) {
updateBody.CPUSockets = plan.Sockets.ValueInt64Pointer() updateBody.CPUSockets = plan.Sockets.ValueInt64Pointer()
} }
} }
if !plan.Units.Equal(state.Units) { if !plan.Units.Equal(state.Units) {
if shouldBeRemoved(plan.Units, state.Units, isClone) { if attribute.ShouldBeRemoved(plan.Units, state.Units, isClone) {
del("CPUUnits") del("CPUUnits")
} else if isDefined(plan.Units) { } else if attribute.IsDefined(plan.Units) {
updateBody.CPUUnits = plan.Units.ValueInt64Pointer() updateBody.CPUUnits = plan.Units.ValueInt64Pointer()
} }
} }
if !plan.Numa.Equal(state.Numa) { if !plan.Numa.Equal(state.Numa) {
if shouldBeRemoved(plan.Numa, state.Numa, isClone) { if attribute.ShouldBeRemoved(plan.Numa, state.Numa, isClone) {
del("NUMAEnabled") del("NUMAEnabled")
} else if isDefined(plan.Numa) { } else if attribute.IsDefined(plan.Numa) {
updateBody.NUMAEnabled = proxmoxtypes.CustomBoolPtr(plan.Numa.ValueBoolPointer()) updateBody.NUMAEnabled = proxmoxtypes.CustomBoolPtr(plan.Numa.ValueBoolPointer())
} }
} }
if !plan.Hotplugged.Equal(state.Hotplugged) { if !plan.Hotplugged.Equal(state.Hotplugged) {
if shouldBeRemoved(plan.Hotplugged, state.Hotplugged, isClone) { if attribute.ShouldBeRemoved(plan.Hotplugged, state.Hotplugged, isClone) {
del("VirtualCPUCount") del("VirtualCPUCount")
} else if isDefined(plan.Hotplugged) { } else if attribute.IsDefined(plan.Hotplugged) {
updateBody.VirtualCPUCount = plan.Hotplugged.ValueInt64Pointer() updateBody.VirtualCPUCount = plan.Hotplugged.ValueInt64Pointer()
} }
} }
@ -221,17 +221,17 @@ func FillUpdateBody(
cpuEmulation := &vms.CustomCPUEmulation{} cpuEmulation := &vms.CustomCPUEmulation{}
if !plan.Type.Equal(state.Type) { if !plan.Type.Equal(state.Type) {
if shouldBeRemoved(plan.Type, state.Type, isClone) { if attribute.ShouldBeRemoved(plan.Type, state.Type, isClone) {
delType = true delType = true
} else if isDefined(plan.Type) { } else if attribute.IsDefined(plan.Type) {
cpuEmulation.Type = plan.Type.ValueString() cpuEmulation.Type = plan.Type.ValueString()
} }
} }
if !plan.Flags.Equal(state.Flags) { if !plan.Flags.Equal(state.Flags) {
if shouldBeRemoved(plan.Flags, state.Flags, isClone) { if attribute.ShouldBeRemoved(plan.Flags, state.Flags, isClone) {
delFlags = true delFlags = true
} else if isDefined(plan.Flags) { } else if attribute.IsDefined(plan.Flags) {
d = plan.Flags.ElementsAs(ctx, &cpuEmulation.Flags, false) d = plan.Flags.ElementsAs(ctx, &cpuEmulation.Flags, false)
diags.Append(d...) diags.Append(d...)
} }
@ -246,11 +246,3 @@ func FillUpdateBody(
updateBody.CPUEmulation = cpuEmulation updateBody.CPUEmulation = cpuEmulation
} }
} }
func shouldBeRemoved(plan attr.Value, state attr.Value, isClone bool) bool {
return !isDefined(plan) && isDefined(state) && !isClone
}
func isDefined(v attr.Value) bool {
return !v.IsNull() && !v.IsUnknown()
}

View File

@ -21,6 +21,7 @@ func TestAccResourceVM2CPU(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
name = "test-cpu" name = "test-cpu"
}`), }`),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
@ -36,6 +37,7 @@ func TestAccResourceVM2CPU(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
name = "test-cpu" name = "test-cpu"
cpu = { cpu = {
cores = 2 cores = 2
@ -59,6 +61,7 @@ func TestAccResourceVM2CPU(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
name = "test-cpu" name = "test-cpu"
cpu = { cpu = {
# affinity = "0-1" only root can set affinity # affinity = "0-1" only root can set affinity
@ -124,6 +127,7 @@ func TestAccResourceVM2CPU(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "template_vm" { resource "proxmox_virtual_environment_vm2" "template_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID1}}
name = "template-cpu" name = "template-cpu"
cpu = { cpu = {
cores = 2 cores = 2
@ -132,7 +136,8 @@ func TestAccResourceVM2CPU(t *testing.T) {
} }
} }
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID2}}
name = "test-cpu" name = "test-cpu"
clone = { clone = {
id = proxmox_virtual_environment_vm2.template_vm.id id = proxmox_virtual_environment_vm2.template_vm.id
@ -150,6 +155,7 @@ func TestAccResourceVM2CPU(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "template_vm" { resource "proxmox_virtual_environment_vm2" "template_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID1}}
name = "template-cpu" name = "template-cpu"
cpu = { cpu = {
cores = 2 cores = 2
@ -158,7 +164,8 @@ func TestAccResourceVM2CPU(t *testing.T) {
} }
} }
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID2}}
name = "test-cpu" name = "test-cpu"
clone = { clone = {
id = proxmox_virtual_environment_vm2.template_vm.id id = proxmox_virtual_environment_vm2.template_vm.id

View File

@ -9,6 +9,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset" "github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu" "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/vga"
) )
// Schema defines the schema for the resource. // Schema defines the schema for the resource.
@ -60,6 +61,7 @@ func (d *Datasource) Schema(
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{ "timeouts": timeouts.Attributes(ctx, timeouts.Opts{
Read: true, Read: true,
}), }),
"vga": vga.DataSourceSchema(),
}, },
} }
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu" "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/vga"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/api" "github.com/bpg/terraform-provider-proxmox/proxmox/api"
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
@ -146,6 +147,7 @@ func (r *Resource) create(ctx context.Context, plan Model, diags *diag.Diagnosti
// fill out create body fields with values from other resource blocks // fill out create body fields with values from other resource blocks
cpu.FillCreateBody(ctx, plan.CPU, createBody, diags) cpu.FillCreateBody(ctx, plan.CPU, createBody, diags)
vga.FillCreateBody(ctx, plan.VGA, createBody, diags)
if diags.HasError() { if diags.HasError() {
return return
@ -321,6 +323,7 @@ func (r *Resource) update(ctx context.Context, plan, state Model, isClone bool,
} }
cpu.FillUpdateBody(ctx, plan.CPU, state.CPU, updateBody, isClone, diags) cpu.FillUpdateBody(ctx, plan.CPU, state.CPU, updateBody, isClone, diags)
vga.FillUpdateBody(ctx, plan.VGA, state.VGA, updateBody, isClone, diags)
if !updateBody.IsEmpty() { if !updateBody.IsEmpty() {
updateBody.VMID = int(plan.ID.ValueInt64()) updateBody.VMID = int(plan.ID.ValueInt64())

View File

@ -17,6 +17,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset" "github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu" "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/vga"
) )
// Schema defines the schema for the resource. // Schema defines the schema for the resource.
@ -93,6 +94,7 @@ func (r *Resource) Schema(
Update: true, Update: true,
Delete: true, Delete: true,
}), }),
"vga": vga.ResourceSchema(),
}, },
} }
} }

View File

@ -62,9 +62,8 @@ func TestAccResourceVM(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
name = "not a valid DNS name" name = "not a valid DNS name"
}`), }`),
ExpectError: regexp.MustCompile(`name must be a valid DNS name`), ExpectError: regexp.MustCompile(`name must be a valid DNS name`),
}}}, }}},
@ -73,7 +72,7 @@ func TestAccResourceVM(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
name = "test-vm" name = "test-vm"
description = "test description" description = "test description"
}`), }`),
@ -86,7 +85,6 @@ func TestAccResourceVM(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
name = "test-vm" name = "test-vm"
}`), }`),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
@ -110,7 +108,7 @@ func TestAccResourceVM(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
name = "test-tags" name = "test-tags"
tags = ["tag2", "tag1"] tags = ["tag2", "tag1"]
}`), }`),
@ -159,7 +157,7 @@ func TestAccResourceVM(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
tags = ["", "tag1"] tags = ["", "tag1"]
}`), }`),
ExpectError: regexp.MustCompile(`string length must be at least 1, got: 0`), ExpectError: regexp.MustCompile(`string length must be at least 1, got: 0`),
@ -168,7 +166,7 @@ func TestAccResourceVM(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
tags = [" ", "tag1"] tags = [" ", "tag1"]
}`), }`),
ExpectError: regexp.MustCompile(`must be a non-empty and non-whitespace string`), ExpectError: regexp.MustCompile(`must be a non-empty and non-whitespace string`),
@ -177,7 +175,7 @@ func TestAccResourceVM(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
description = trimspace(<<-EOT description = trimspace(<<-EOT
my my
description description
@ -220,12 +218,14 @@ func TestAccResourceVM2Clone(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID1}}
name = "template" name = "template"
description = "template description" description = "template description"
template = true template = true
} }
resource "proxmox_virtual_environment_vm2" "test_vm_clone" { resource "proxmox_virtual_environment_vm2" "test_vm_clone" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID2}}
name = "clone" name = "clone"
clone = { clone = {
id = proxmox_virtual_environment_vm2.test_vm.id id = proxmox_virtual_environment_vm2.test_vm.id
@ -249,11 +249,13 @@ func TestAccResourceVM2Clone(t *testing.T) {
Config: te.RenderConfig(` Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" { resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID1}}
template = true template = true
tags = ["tag1", "tag2"] tags = ["tag1", "tag2"]
} }
resource "proxmox_virtual_environment_vm2" "test_vm_clone" { resource "proxmox_virtual_environment_vm2" "test_vm_clone" {
node_name = "{{.NodeName}}" node_name = "{{.NodeName}}"
id = {{.RandomVMID2}}
clone = { clone = {
id = proxmox_virtual_environment_vm2.test_vm.id id = proxmox_virtual_environment_vm2.test_vm.id
} }

View File

@ -0,0 +1,36 @@
package vga
import (
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)
// DataSourceSchema defines the schema for the VGA resource.
func DataSourceSchema() schema.Attribute {
return schema.SingleNestedAttribute{
CustomType: basetypes.ObjectType{
AttrTypes: attributeTypes(),
},
Description: "The VGA configuration.",
Optional: true,
Computed: true,
Attributes: map[string]schema.Attribute{
"clipboard": schema.StringAttribute{
Description: "Enable a specific clipboard.",
Optional: true,
Computed: true,
},
"type": schema.StringAttribute{
Description: "The VGA type.",
Optional: true,
Computed: true,
},
"memory": schema.Int64Attribute{
Description: "The VGA memory in megabytes (4-512 MB)",
MarkdownDescription: "The VGA memory in megabytes (4-512 MB). Has no effect with serial display. ",
Optional: true,
Computed: true,
},
},
}
}

View File

@ -0,0 +1,129 @@
package vga
import (
"context"
"reflect"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/bpg/terraform-provider-proxmox/fwprovider/attribute"
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
)
// Value represents the type for VGA settings.
type Value = types.Object
// NewValue returns a new Value with the given VGA settings from the PVE API.
func NewValue(ctx context.Context, config *vms.GetResponseData, diags *diag.Diagnostics) Value {
vga := Model{}
if config.VGADevice != nil {
vga.Clipboard = types.StringPointerValue(config.VGADevice.Clipboard)
vga.Type = types.StringPointerValue(config.VGADevice.Type)
vga.Memory = types.Int64PointerValue(config.VGADevice.Memory)
}
obj, d := types.ObjectValueFrom(ctx, attributeTypes(), vga)
diags.Append(d...)
return obj
}
// FillCreateBody fills the CreateRequestBody with the VGA settings from the Value.
//
// In the 'create' context, v is the plan.
func FillCreateBody(ctx context.Context, planValue Value, body *vms.CreateRequestBody, diags *diag.Diagnostics) {
var plan Model
if planValue.IsNull() || planValue.IsUnknown() {
return
}
d := planValue.As(ctx, &plan, basetypes.ObjectAsOptions{})
diags.Append(d...)
if d.HasError() {
return
}
vgaDevice := &vms.CustomVGADevice{}
// for computed fields, we need to check if they are unknown
if !plan.Clipboard.IsUnknown() {
vgaDevice.Clipboard = plan.Clipboard.ValueStringPointer()
}
if !plan.Type.IsUnknown() {
vgaDevice.Type = plan.Type.ValueStringPointer()
}
if !plan.Memory.IsUnknown() {
vgaDevice.Memory = plan.Memory.ValueInt64Pointer()
}
if !reflect.DeepEqual(vgaDevice, &vms.CustomVGADevice{}) {
body.VGADevice = vgaDevice
}
}
// FillUpdateBody fills the UpdateRequestBody with the VGA settings from the Value.
//
// In the 'update' context, v is the plan and stateValue is the current state.
func FillUpdateBody(
ctx context.Context,
planValue, stateValue Value,
updateBody *vms.UpdateRequestBody,
isClone bool,
diags *diag.Diagnostics,
) {
var plan, state Model
if planValue.IsNull() || planValue.IsUnknown() || planValue.Equal(stateValue) {
return
}
d := planValue.As(ctx, &plan, basetypes.ObjectAsOptions{})
diags.Append(d...)
d = stateValue.As(ctx, &state, basetypes.ObjectAsOptions{})
diags.Append(d...)
if diags.HasError() {
return
}
vgaDevice := &vms.CustomVGADevice{
Clipboard: state.Clipboard.ValueStringPointer(),
Type: state.Type.ValueStringPointer(),
Memory: state.Memory.ValueInt64Pointer(),
}
if !plan.Clipboard.Equal(state.Clipboard) {
if attribute.ShouldBeRemoved(plan.Clipboard, state.Clipboard, isClone) {
vgaDevice.Clipboard = nil
} else if attribute.IsDefined(plan.Clipboard) {
vgaDevice.Clipboard = plan.Clipboard.ValueStringPointer()
}
}
if !plan.Type.Equal(state.Type) {
if attribute.ShouldBeRemoved(plan.Type, state.Type, isClone) {
vgaDevice.Type = nil
} else if attribute.IsDefined(plan.Type) {
vgaDevice.Type = plan.Type.ValueStringPointer()
}
}
if !plan.Memory.Equal(state.Memory) {
if attribute.ShouldBeRemoved(plan.Memory, state.Memory, isClone) {
vgaDevice.Memory = nil
} else if attribute.IsDefined(plan.Memory) {
vgaDevice.Memory = plan.Memory.ValueInt64Pointer()
}
}
if !reflect.DeepEqual(vgaDevice, &vms.CustomVGADevice{}) {
updateBody.VGADevice = vgaDevice
}
}

View File

@ -0,0 +1,21 @@
package vga
import (
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
)
// Model represents the VGA model.
type Model struct {
Clipboard types.String `tfsdk:"clipboard"`
Type types.String `tfsdk:"type"`
Memory types.Int64 `tfsdk:"memory"`
}
func attributeTypes() map[string]attr.Type {
return map[string]attr.Type{
"clipboard": types.StringType,
"type": types.StringType,
"memory": types.Int64Type,
}
}

View File

@ -0,0 +1,79 @@
package vga
import (
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)
// ResourceSchema defines the schema for the CPU resource.
func ResourceSchema() schema.Attribute {
return schema.SingleNestedAttribute{
CustomType: basetypes.ObjectType{
AttrTypes: attributeTypes(),
},
Description: "The VGA configuration.",
MarkdownDescription: "Configure the VGA Hardware. If you want to use high resolution modes (>= 1280x1024x16) " +
"you may need to increase the vga memory option. Since QEMU 2.9 the default VGA display type is `std` " +
"for all OS types besides some Windows versions (XP and older) which use `cirrus`. The `qxl` option " +
"enables the SPICE display server. For win* OS you can select how many independent displays you want, " +
"Linux guests can add displays themself. You can also run without any graphic card, using a serial device " +
"as terminal. See the [Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#" +
"qm_virtual_machines_settings) section 10.2.8 for more information and available configuration parameters.",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.Object{
objectplanmodifier.UseStateForUnknown(),
},
Attributes: map[string]schema.Attribute{
"clipboard": schema.StringAttribute{
Description: "Enable a specific clipboard.",
MarkdownDescription: "Enable a specific clipboard. If not set, depending on the display type the SPICE " +
"one will be added. Currently only `vnc` is available. Migration with VNC clipboard is not " +
"supported by Proxmox.",
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.OneOf("vnc"),
},
},
"type": schema.StringAttribute{
Description: "The VGA type.",
MarkdownDescription: "The VGA type (defaults to `std`).",
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.OneOf(
"cirrus",
"none",
"qxl",
"qxl2",
"qxl3",
"qxl4",
"serial0",
"serial1",
"serial2",
"serial3",
"std",
"virtio",
"virtio-gl",
"vmware",
),
},
},
"memory": schema.Int64Attribute{
Description: "The VGA memory in megabytes (4-512 MB)",
MarkdownDescription: "The VGA memory in megabytes (4-512 MB). Has no effect with serial display. ",
Optional: true,
Computed: true,
Validators: []validator.Int64{
int64validator.Between(4, 512),
},
},
},
}
}

View File

@ -0,0 +1,162 @@
package vga_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/bpg/terraform-provider-proxmox/fwprovider/test"
)
func TestAccResourceVM2VGA(t *testing.T) {
t.Parallel()
te := test.InitEnvironment(t)
tests := []struct {
name string
steps []resource.TestStep
}{
{"create VM with no vga params", []resource.TestStep{{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
name = "test-vga"
}`),
Check: test.NoResourceAttributesSet("proxmox_virtual_environment_vm2.test_vm", []string{
// PVE does not set / return anything by default
"vga.type",
}),
}}},
{"create VM with some vga params", []resource.TestStep{{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
name = "test-vga"
vga = {
type = "std"
}
}`),
Check: resource.ComposeTestCheckFunc(
test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
"vga.type": "std",
}),
test.NoResourceAttributesSet("proxmox_virtual_environment_vm2.test_vm", []string{
"vga.clipboard",
"vga.memory",
}),
),
}}},
{"create VM with VGA params and then update them", []resource.TestStep{
{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}"
id = {{.RandomVMID}}
name = "test-vga"
vga = {
type = "std"
memory = 16
}
}`),
Check: resource.ComposeTestCheckFunc(
test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
"vga.type": "std",
"vga.memory": "16",
}),
),
},
{ // now update the vga params and check if they are updated
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}"
name = "test-cpu"
vga = {
type = "qxl"
clipboard = "vnc"
}
}`),
Check: resource.ComposeTestCheckFunc(
test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
"vga.type": "qxl",
"vga.clipboard": "vnc",
}),
test.NoResourceAttributesSet("proxmox_virtual_environment_vm2.test_vm", []string{
"vga.memory",
}),
),
},
{
RefreshState: true,
},
}},
{"clone VM with some vga params", []resource.TestStep{{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "template_vm" {
node_name = "{{.NodeName}}"
id = {{.RandomVMID1}}
name = "template-vga"
vga = {
type = "qxl"
clipboard = "vnc"
}
}
resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}"
id = {{.RandomVMID2}}
name = "test-vga"
clone = {
id = proxmox_virtual_environment_vm2.template_vm.id
}
}`),
Check: resource.ComposeTestCheckFunc(
test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
"vga.type": "qxl",
"vga.clipboard": "vnc",
}),
),
}}},
{"clone VM with some vga params and updating them in the clone", []resource.TestStep{{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm2" "template_vm" {
node_name = "{{.NodeName}}"
id = {{.RandomVMID1}}
name = "template-vga"
vga = {
type = "qxl"
clipboard = "vnc"
}
}
resource "proxmox_virtual_environment_vm2" "test_vm" {
node_name = "{{.NodeName}}"
name = "test-cpu"
id = {{.RandomVMID2}}
clone = {
id = proxmox_virtual_environment_vm2.template_vm.id
}
vga = {
type = "std"
memory = 16
}
}`),
Check: resource.ComposeTestCheckFunc(
test.ResourceAttributes("proxmox_virtual_environment_vm2.test_vm", map[string]string{
"vga.type": "std",
"vga.memory": "16",
"vga.clipboard": "vnc",
}),
),
}}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: te.AccProviders,
Steps: tt.steps,
})
})
}
}

View File

@ -11,6 +11,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset" "github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu" "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/vga"
"github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/api" "github.com/bpg/terraform-provider-proxmox/proxmox/api"
) )
@ -32,6 +33,7 @@ type Model struct {
Tags stringset.Value `tfsdk:"tags"` Tags stringset.Value `tfsdk:"tags"`
Template types.Bool `tfsdk:"template"` Template types.Bool `tfsdk:"template"`
Timeouts timeouts.Value `tfsdk:"timeouts"` Timeouts timeouts.Value `tfsdk:"timeouts"`
VGA vga.Value `tfsdk:"vga"`
} }
// read retrieves the current state of the resource from the API and updates the state. // read retrieves the current state of the resource from the API and updates the state.
@ -69,9 +71,12 @@ func read(ctx context.Context, client proxmox.Client, model *Model, diags *diag.
// Optional fields can be removed from the model, use StringPointerValue to handle removal on nil // Optional fields can be removed from the model, use StringPointerValue to handle removal on nil
model.Description = types.StringPointerValue(config.Description) model.Description = types.StringPointerValue(config.Description)
model.Name = types.StringPointerValue(config.Name) model.Name = types.StringPointerValue(config.Name)
model.CPU = cpu.NewValue(ctx, config, diags)
model.Tags = stringset.NewValue(config.Tags, diags) model.Tags = stringset.NewValue(config.Tags, diags)
model.Template = types.BoolPointerValue(config.Template.PointerBool()) model.Template = types.BoolPointerValue(config.Template.PointerBool())
// Blocks
model.CPU = cpu.NewValue(ctx, config, diags)
model.VGA = vga.NewValue(ctx, config, diags)
return true return true
} }

View File

@ -186,7 +186,7 @@ type CustomUSBDevices []CustomUSBDevice
// CustomVGADevice handles QEMU VGA device parameters. // CustomVGADevice handles QEMU VGA device parameters.
type CustomVGADevice struct { type CustomVGADevice struct {
Clipboard *string `json:"clipboard,omitempty" url:"memory,omitempty"` Clipboard *string `json:"clipboard,omitempty" url:"memory,omitempty"`
Memory *int `json:"memory,omitempty" url:"memory,omitempty"` Memory *int64 `json:"memory,omitempty" url:"memory,omitempty"`
Type *string `json:"type,omitempty" url:"type,omitempty"` Type *string `json:"type,omitempty" url:"type,omitempty"`
} }
@ -2047,7 +2047,7 @@ func (r *CustomVGADevice) UnmarshalJSON(b []byte) error {
r.Clipboard = &v[1] r.Clipboard = &v[1]
case "memory": case "memory":
m, err := strconv.Atoi(v[1]) m, err := strconv.ParseInt(v[1], 10, 64)
if err != nil { if err != nil {
return fmt.Errorf("failed to convert memory to int: %w", err) return fmt.Errorf("failed to convert memory to int: %w", err)
} }

View File

@ -3357,7 +3357,7 @@ func vmGetVGADeviceObject(d *schema.ResourceData) (*vms.CustomVGADevice, error)
} }
if vgaMemory > 0 { if vgaMemory > 0 {
vgaDevice.Memory = &vgaMemory vgaDevice.Memory = ptr.Ptr(int64(vgaMemory))
} }
if vgaType != "" { if vgaType != "" {
@ -4429,7 +4429,7 @@ func vmReadCustom(
} }
if vmConfig.VGADevice.Memory != nil { if vmConfig.VGADevice.Memory != nil {
vga[mkVGAMemory] = *vmConfig.VGADevice.Memory vga[mkVGAMemory] = int(*vmConfig.VGADevice.Memory)
} else { } else {
vga[mkVGAMemory] = dvVGAMemory vga[mkVGAMemory] = dvVGAMemory
} }