From e3dd31f55e41047e004ebff34a713a4e9ed8616f Mon Sep 17 00:00:00 2001
From: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
Date: Tue, 21 May 2024 22:06:41 -0400
Subject: [PATCH] chore(vm2): add datasource implementation (#1318)
* chore(vm2): add datasource implementation
---------
Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
---
docs/data-sources/virtual_environment_vm2.md | 69 ++++++++++++++
docs/resources/virtual_environment_vm2.md | 2 +-
fwprovider/provider.go | 19 ++--
fwprovider/vm/cpu/datasource_schema.go | 72 ++++++++++++++
.../vm/cpu/{resource_cpu.go => resource.go} | 0
...esource_cpu_model.go => resource_model.go} | 0
...ource_cpu_schema.go => resource_schema.go} | 7 +-
...{resource_cpu_test.go => resource_test.go} | 0
fwprovider/vm/datasource.go | 94 +++++++++++++++++++
fwprovider/vm/datasource_schema.go | 65 +++++++++++++
fwprovider/vm/{resource_vm.go => resource.go} | 58 ++----------
...source_vm_schema.go => resource_schema.go} | 4 +-
.../{resource_vm_test.go => resource_test.go} | 0
fwprovider/vm/resource_vm_model.go | 28 ------
fwprovider/vm/vm_model.go | 77 +++++++++++++++
.../virtual_environment_vm2.md.tmpl | 21 +++++
tools/tools.go | 13 +--
17 files changed, 430 insertions(+), 99 deletions(-)
create mode 100644 docs/data-sources/virtual_environment_vm2.md
create mode 100644 fwprovider/vm/cpu/datasource_schema.go
rename fwprovider/vm/cpu/{resource_cpu.go => resource.go} (100%)
rename fwprovider/vm/cpu/{resource_cpu_model.go => resource_model.go} (100%)
rename fwprovider/vm/cpu/{resource_cpu_schema.go => resource_schema.go} (96%)
rename fwprovider/vm/cpu/{resource_cpu_test.go => resource_test.go} (100%)
create mode 100644 fwprovider/vm/datasource.go
create mode 100644 fwprovider/vm/datasource_schema.go
rename fwprovider/vm/{resource_vm.go => resource.go} (87%)
rename fwprovider/vm/{resource_vm_schema.go => resource_schema.go} (98%)
rename fwprovider/vm/{resource_vm_test.go => resource_test.go} (100%)
delete mode 100644 fwprovider/vm/resource_vm_model.go
create mode 100644 fwprovider/vm/vm_model.go
create mode 100644 templates/data-sources/virtual_environment_vm2.md.tmpl
diff --git a/docs/data-sources/virtual_environment_vm2.md b/docs/data-sources/virtual_environment_vm2.md
new file mode 100644
index 00000000..25745dc0
--- /dev/null
+++ b/docs/data-sources/virtual_environment_vm2.md
@@ -0,0 +1,69 @@
+---
+layout: page
+title: proxmox_virtual_environment_vm2
+parent: Data Sources
+subcategory: Virtual Environment
+description: |-
+ This is an experimental implementation of a Proxmox VM datasource using Plugin Framework.
+---
+
+# Data Source: proxmox_virtual_environment_vm2
+
+!> **DO NOT USE**
+This is an experimental implementation of a Proxmox VM datasource using Plugin Framework.
+
+
+
+
+## Schema
+
+### Required
+
+- `id` (Number) The unique identifier of the VM in the Proxmox cluster.
+- `node_name` (String) The name of the node where the VM is provisioned.
+
+### Optional
+
+- `clone` (Attributes) The cloning configuration. (see [below for nested schema](#nestedatt--clone))
+- `cpu` (Attributes) The CPU configuration. (see [below for nested schema](#nestedatt--cpu))
+- `description` (String) The description of the VM.
+- `name` (String) The name of the VM.
+- `tags` (Set of String) The tags assigned to the VM.
+- `template` (Boolean) Whether the VM is a template.
+- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
+
+
+### Nested Schema for `clone`
+
+Required:
+
+- `id` (Number) The ID of the VM to clone.
+
+Optional:
+
+- `retries` (Number) The number of retries to perform when cloning the VM (default: 3).
+
+
+
+### Nested Schema for `cpu`
+
+Optional:
+
+- `affinity` (String) List of host cores used to execute guest processes, for example: '0,5,8-11'
+- `architecture` (String) The CPU architecture.
+- `cores` (Number) The number of CPU cores per socket.
+- `flags` (Set of String) Set of additional CPU flags.
+- `hotplugged` (Number) The number of hotplugged vCPUs.
+- `limit` (Number) Limit of CPU usage.
+- `numa` (Boolean) Enable NUMA.
+- `sockets` (Number) The number of CPU sockets.
+- `type` (String) Emulated CPU type.
+- `units` (Number) CPU weight for a VM
+
+
+
+### Nested Schema for `timeouts`
+
+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.
diff --git a/docs/resources/virtual_environment_vm2.md b/docs/resources/virtual_environment_vm2.md
index 5cce6a3c..77f614c1 100644
--- a/docs/resources/virtual_environment_vm2.md
+++ b/docs/resources/virtual_environment_vm2.md
@@ -35,7 +35,7 @@ The attributes are also marked as optional to allow the practitioner to set (or
- `description` (String) The description of the VM.
- `id` (Number) The unique identifier of the VM in the Proxmox cluster.
- `name` (String) The name of the VM. Doesn't have to be unique.
-- `tags` (Set of String) The tags assigned to the resource.
+- `tags` (Set of String) The tags assigned to the VM.
- `template` (Boolean) Set to true to create a VM template.
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
diff --git a/fwprovider/provider.go b/fwprovider/provider.go
index e0d1d0d4..792c7761 100644
--- a/fwprovider/provider.go
+++ b/fwprovider/provider.go
@@ -441,30 +441,31 @@ func (p *proxmoxProvider) Configure(
func (p *proxmoxProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
+ NewClusterOptionsResource,
+ NewDownloadFileResource,
+ access.NewACLResource,
+ access.NewUserTokenResource,
ha.NewHAGroupResource,
ha.NewHAResourceResource,
hardwaremapping.NewResourcePCI,
hardwaremapping.NewResourceUSB,
network.NewLinuxBridgeResource,
network.NewLinuxVLANResource,
- access.NewUserTokenResource,
- vm.NewVMResource,
- NewClusterOptionsResource,
- NewDownloadFileResource,
- access.NewACLResource,
+ vm.NewResource,
}
}
func (p *proxmoxProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
- ha.NewHAGroupsDataSource,
+ NewVersionDataSource,
ha.NewHAGroupDataSource,
- ha.NewHAResourcesDataSource,
+ ha.NewHAGroupsDataSource,
ha.NewHAResourceDataSource,
+ ha.NewHAResourcesDataSource,
+ hardwaremapping.NewDataSource,
hardwaremapping.NewDataSourcePCI,
hardwaremapping.NewDataSourceUSB,
- hardwaremapping.NewDataSource,
- NewVersionDataSource,
+ vm.NewDataSource,
}
}
diff --git a/fwprovider/vm/cpu/datasource_schema.go b/fwprovider/vm/cpu/datasource_schema.go
new file mode 100644
index 00000000..33f48fc7
--- /dev/null
+++ b/fwprovider/vm/cpu/datasource_schema.go
@@ -0,0 +1,72 @@
+package cpu
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+)
+
+// DataSourceSchema defines the schema for the CPU resource.
+func DataSourceSchema() schema.Attribute {
+ return schema.SingleNestedAttribute{
+ CustomType: basetypes.ObjectType{
+ AttrTypes: attributeTypes(),
+ },
+ Description: "The CPU configuration.",
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "affinity": schema.StringAttribute{
+ Description: "List of host cores used to execute guest processes, for example: '0,5,8-11'",
+ Optional: true,
+ Computed: true,
+ },
+ "architecture": schema.StringAttribute{
+ Description: "The CPU architecture.",
+ Optional: true,
+ Computed: true,
+ },
+ "cores": schema.Int64Attribute{
+ Description: "The number of CPU cores per socket.",
+ Optional: true,
+ Computed: true,
+ },
+ "flags": schema.SetAttribute{
+ Description: "Set of additional CPU flags.",
+ Optional: true,
+ Computed: true,
+ ElementType: types.StringType,
+ },
+ "hotplugged": schema.Int64Attribute{
+ Description: "The number of hotplugged vCPUs.",
+ Optional: true,
+ Computed: true,
+ },
+ "limit": schema.Int64Attribute{
+ Description: "Limit of CPU usage.",
+ Optional: true,
+ Computed: true,
+ },
+ "numa": schema.BoolAttribute{
+ Description: "Enable NUMA.",
+ Optional: true,
+ Computed: true,
+ },
+ "sockets": schema.Int64Attribute{
+ Description: "The number of CPU sockets.",
+ Optional: true,
+ Computed: true,
+ },
+ "type": schema.StringAttribute{
+ Description: "Emulated CPU type.",
+ Optional: true,
+ Computed: true,
+ },
+ "units": schema.Int64Attribute{
+ Description: "CPU weight for a VM",
+ Optional: true,
+ Computed: true,
+ },
+ },
+ }
+}
diff --git a/fwprovider/vm/cpu/resource_cpu.go b/fwprovider/vm/cpu/resource.go
similarity index 100%
rename from fwprovider/vm/cpu/resource_cpu.go
rename to fwprovider/vm/cpu/resource.go
diff --git a/fwprovider/vm/cpu/resource_cpu_model.go b/fwprovider/vm/cpu/resource_model.go
similarity index 100%
rename from fwprovider/vm/cpu/resource_cpu_model.go
rename to fwprovider/vm/cpu/resource_model.go
diff --git a/fwprovider/vm/cpu/resource_cpu_schema.go b/fwprovider/vm/cpu/resource_schema.go
similarity index 96%
rename from fwprovider/vm/cpu/resource_cpu_schema.go
rename to fwprovider/vm/cpu/resource_schema.go
index cf14c305..abafcca5 100644
--- a/fwprovider/vm/cpu/resource_cpu_schema.go
+++ b/fwprovider/vm/cpu/resource_schema.go
@@ -15,8 +15,8 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)
-// Schema defines the schema for the CPU resource.
-func Schema() schema.Attribute {
+// ResourceSchema defines the schema for the CPU resource.
+func ResourceSchema() schema.Attribute {
return schema.SingleNestedAttribute{
CustomType: basetypes.ObjectType{
AttrTypes: attributeTypes(),
@@ -202,7 +202,8 @@ func Schema() schema.Attribute {
},
},
"units": schema.Int64Attribute{
- Description: "CPU weight for a VM. Argument is used in the kernel fair scheduler. " +
+ Description: "CPU weight for a VM.",
+ MarkdownDescription: "CPU weight for a VM. Argument is used in the kernel fair scheduler. " +
"The larger the number is, the more CPU time this VM gets. " +
"Number is relative to weights of all the other running VMs.",
Optional: true,
diff --git a/fwprovider/vm/cpu/resource_cpu_test.go b/fwprovider/vm/cpu/resource_test.go
similarity index 100%
rename from fwprovider/vm/cpu/resource_cpu_test.go
rename to fwprovider/vm/cpu/resource_test.go
diff --git a/fwprovider/vm/datasource.go b/fwprovider/vm/datasource.go
new file mode 100644
index 00000000..364aaae8
--- /dev/null
+++ b/fwprovider/vm/datasource.go
@@ -0,0 +1,94 @@
+package vm
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+
+ "github.com/bpg/terraform-provider-proxmox/proxmox"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ datasource.DataSource = &Datasource{}
+ _ datasource.DataSourceWithConfigure = &Datasource{}
+)
+
+// Datasource is the implementation of VM datasource.
+type Datasource struct {
+ client proxmox.Client
+}
+
+// NewDataSource creates a new VM datasource.
+func NewDataSource() datasource.DataSource {
+ return &Datasource{}
+}
+
+// Metadata defines the name of the resource.
+func (d *Datasource) Metadata(
+ _ context.Context,
+ req datasource.MetadataRequest,
+ resp *datasource.MetadataResponse,
+) {
+ resp.TypeName = req.ProviderTypeName + "_vm2"
+}
+
+// Configure sets the client for the resource.
+func (d *Datasource) Configure(
+ _ context.Context,
+ req datasource.ConfigureRequest,
+ resp *datasource.ConfigureResponse,
+) {
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(proxmox.Client)
+
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Resource Configure Type",
+ fmt.Sprintf("Expected *proxmox.Client, got: %T", req.ProviderData),
+ )
+
+ return
+ }
+
+ d.client = client
+}
+
+//nolint:dupl
+func (d *Datasource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var config Model
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ timeout, diags := config.Timeouts.Read(ctx, defaultReadTimeout)
+ resp.Diagnostics.Append(diags...)
+
+ ctx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+
+ exists := read(ctx, d.client, &config, &resp.Diagnostics)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ if !exists {
+ tflog.Info(ctx, "VM does not exist, removing from the state", map[string]interface{}{
+ "id": config.ID.ValueInt64(),
+ })
+ resp.State.RemoveResource(ctx)
+
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, config)...)
+}
diff --git a/fwprovider/vm/datasource_schema.go b/fwprovider/vm/datasource_schema.go
new file mode 100644
index 00000000..0b3d82a7
--- /dev/null
+++ b/fwprovider/vm/datasource_schema.go
@@ -0,0 +1,65 @@
+package vm
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
+)
+
+// Schema defines the schema for the resource.
+func (d *Datasource) Schema(
+ ctx context.Context,
+ _ datasource.SchemaRequest,
+ resp *datasource.SchemaResponse,
+) {
+ resp.Schema = schema.Schema{
+ Description: "This is an experimental implementation of a Proxmox VM datasource using Plugin Framework.",
+ Attributes: map[string]schema.Attribute{
+ "clone": schema.SingleNestedAttribute{
+ Description: "The cloning configuration.",
+ Optional: true,
+ Attributes: map[string]schema.Attribute{
+ "id": schema.Int64Attribute{
+ Description: "The ID of the VM to clone.",
+ Required: true,
+ },
+ "retries": schema.Int64Attribute{
+ Description: "The number of retries to perform when cloning the VM (default: 3).",
+ Optional: true,
+ Computed: true,
+ },
+ },
+ },
+ "cpu": cpu.DataSourceSchema(),
+ "description": schema.StringAttribute{
+ Description: "The description of the VM.",
+ Optional: true,
+ },
+ "id": schema.Int64Attribute{
+ Required: true,
+ Description: "The unique identifier of the VM in the Proxmox cluster.",
+ },
+ "name": schema.StringAttribute{
+ Description: "The name of the VM.",
+ Optional: true,
+ },
+ "node_name": schema.StringAttribute{
+ Description: "The name of the node where the VM is provisioned.",
+ Required: true,
+ },
+ "tags": stringset.ResourceAttribute("The tags assigned to the VM.", ""),
+ "template": schema.BoolAttribute{
+ Description: "Whether the VM is a template.",
+ Optional: true,
+ },
+ "timeouts": timeouts.Attributes(ctx, timeouts.Opts{
+ Read: true,
+ }),
+ },
+ }
+}
diff --git a/fwprovider/vm/resource_vm.go b/fwprovider/vm/resource.go
similarity index 87%
rename from fwprovider/vm/resource_vm.go
rename to fwprovider/vm/resource.go
index 8b3a1267..402a81bb 100644
--- a/fwprovider/vm/resource_vm.go
+++ b/fwprovider/vm/resource.go
@@ -15,7 +15,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
- "github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
"github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
"github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
@@ -44,8 +43,8 @@ type Resource struct {
client proxmox.Client
}
-// NewVMResource creates a new resource for managing VMs.
-func NewVMResource() resource.Resource {
+// NewResource creates a new resource for managing VMs.
+func NewResource() resource.Resource {
return &Resource{}
}
@@ -123,7 +122,7 @@ func (r *Resource) Create(ctx context.Context, req resource.CreateRequest, resp
}
// read back the VM from the PVE API to populate computed fields
- exists := r.read(ctx, &plan, &resp.Diagnostics)
+ exists := read(ctx, r.client, &plan, &resp.Diagnostics)
if !exists {
resp.Diagnostics.AddError("VM does not exist after creation", "")
}
@@ -195,7 +194,7 @@ func (r *Resource) clone(ctx context.Context, plan Model, diags *diag.Diagnostic
NodeName: plan.NodeName,
}
- r.read(ctx, &clone, diags)
+ read(ctx, r.client, &clone, diags)
if diags.HasError() {
return
@@ -204,6 +203,7 @@ func (r *Resource) clone(ctx context.Context, plan Model, diags *diag.Diagnostic
r.update(ctx, plan, clone, true, diags)
}
+//nolint:dupl
func (r *Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state Model
@@ -219,7 +219,7 @@ func (r *Resource) Read(ctx context.Context, req resource.ReadRequest, resp *res
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
- exists := r.read(ctx, &state, &resp.Diagnostics)
+ exists := read(ctx, r.client, &state, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
@@ -258,7 +258,7 @@ func (r *Resource) Update(ctx context.Context, req resource.UpdateRequest, resp
r.update(ctx, plan, state, false, &resp.Diagnostics)
// read back the VM from the PVE API to populate computed fields
- exists := r.read(ctx, &plan, &resp.Diagnostics)
+ exists := read(ctx, r.client, &plan, &resp.Diagnostics)
if !exists {
resp.Diagnostics.AddError("VM does not exist after update", "")
}
@@ -423,7 +423,7 @@ func (r *Resource) ImportState(
Timeouts: ts,
}
- exists := r.read(ctx, &state, &resp.Diagnostics)
+ exists := read(ctx, r.client, &state, &resp.Diagnostics)
if !exists {
resp.Diagnostics.AddError(fmt.Sprintf("VM %d does not exist on node %s", id, nodeName), "")
}
@@ -436,48 +436,6 @@ func (r *Resource) ImportState(
resp.Diagnostics.Append(diags...)
}
-// read retrieves the current state of the resource from the API and updates the state.
-// Returns false if the resource does not exist, so the caller can remove it from the state if necessary.
-func (r *Resource) read(ctx context.Context, model *Model, diags *diag.Diagnostics) bool {
- vmAPI := r.client.Node(model.NodeName.ValueString()).VM(int(model.ID.ValueInt64()))
-
- // Retrieve the entire configuration in order to compare it to the state.
- config, err := vmAPI.GetVM(ctx)
- if err != nil {
- if errors.Is(err, api.ErrResourceDoesNotExist) {
- tflog.Info(ctx, "VM does not exist, removing from the state", map[string]interface{}{
- "vm_id": vmAPI.VMID,
- })
- } else {
- diags.AddError("Failed to get VM", err.Error())
- }
-
- return false
- }
-
- status, err := vmAPI.GetVMStatus(ctx)
- if err != nil {
- diags.AddError("Failed to get VM status", err.Error())
- return false
- }
-
- if status.VMID == nil {
- diags.AddError("VM ID is missing in status API response", "")
- return false
- }
-
- model.ID = types.Int64Value(int64(*status.VMID))
-
- // Optional fields can be removed from the model, use StringPointerValue to handle removal on nil
- model.Description = types.StringPointerValue(config.Description)
- model.Name = types.StringPointerValue(config.Name)
- model.CPU = cpu.NewValue(ctx, config, diags)
- model.Tags = stringset.NewValue(config.Tags, diags)
- model.Template = types.BoolPointerValue(config.Template.PointerBool())
-
- return true
-}
-
// Shutdown the VM, then wait for it to actually shut down (it may not be shut down immediately if
// running in HA mode).
func vmShutdown(ctx context.Context, vmAPI *vms.Client) error {
diff --git a/fwprovider/vm/resource_vm_schema.go b/fwprovider/vm/resource_schema.go
similarity index 98%
rename from fwprovider/vm/resource_vm_schema.go
rename to fwprovider/vm/resource_schema.go
index 79902b15..a0f46c97 100644
--- a/fwprovider/vm/resource_vm_schema.go
+++ b/fwprovider/vm/resource_schema.go
@@ -50,7 +50,7 @@ func (r *Resource) Schema(
},
},
},
- "cpu": cpu.Schema(),
+ "cpu": cpu.ResourceSchema(),
"description": schema.StringAttribute{
Description: "The description of the VM.",
Optional: true,
@@ -79,7 +79,7 @@ func (r *Resource) Schema(
Description: "The name of the node where the VM is provisioned.",
Required: true,
},
- "tags": stringset.ResourceAttribute("The tags assigned to the resource.", ""),
+ "tags": stringset.ResourceAttribute("The tags assigned to the VM.", ""),
"template": schema.BoolAttribute{
Description: "Set to true to create a VM template.",
Optional: true,
diff --git a/fwprovider/vm/resource_vm_test.go b/fwprovider/vm/resource_test.go
similarity index 100%
rename from fwprovider/vm/resource_vm_test.go
rename to fwprovider/vm/resource_test.go
diff --git a/fwprovider/vm/resource_vm_model.go b/fwprovider/vm/resource_vm_model.go
deleted file mode 100644
index 974cf570..00000000
--- a/fwprovider/vm/resource_vm_model.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package vm
-
-import (
- "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
- "github.com/hashicorp/terraform-plugin-framework/types"
-
- "github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
- "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
-)
-
-// Model represents the VM model.
-//
-// Note: for computed fields / blocks we have to use an Object type (or an alias),
-// or a custom type in order to hold an unknown value.
-type Model struct {
- Description types.String `tfsdk:"description"`
- CPU cpu.Value `tfsdk:"cpu"`
- Clone *struct {
- ID types.Int64 `tfsdk:"id"`
- Retries types.Int64 `tfsdk:"retries"`
- } `tfsdk:"clone"`
- ID types.Int64 `tfsdk:"id"`
- Name types.String `tfsdk:"name"`
- NodeName types.String `tfsdk:"node_name"`
- Tags stringset.Value `tfsdk:"tags"`
- Template types.Bool `tfsdk:"template"`
- Timeouts timeouts.Value `tfsdk:"timeouts"`
-}
diff --git a/fwprovider/vm/vm_model.go b/fwprovider/vm/vm_model.go
new file mode 100644
index 00000000..c6c67b04
--- /dev/null
+++ b/fwprovider/vm/vm_model.go
@@ -0,0 +1,77 @@
+package vm
+
+import (
+ "context"
+ "errors"
+
+ "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/types/stringset"
+ "github.com/bpg/terraform-provider-proxmox/fwprovider/vm/cpu"
+ "github.com/bpg/terraform-provider-proxmox/proxmox"
+ "github.com/bpg/terraform-provider-proxmox/proxmox/api"
+)
+
+// Model represents the VM model.
+//
+// Note: for computed fields / blocks we have to use an Object type (or an alias),
+// or a custom type in order to hold an unknown value.
+type Model struct {
+ Description types.String `tfsdk:"description"`
+ CPU cpu.Value `tfsdk:"cpu"`
+ Clone *struct {
+ ID types.Int64 `tfsdk:"id"`
+ Retries types.Int64 `tfsdk:"retries"`
+ } `tfsdk:"clone"`
+ ID types.Int64 `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ NodeName types.String `tfsdk:"node_name"`
+ Tags stringset.Value `tfsdk:"tags"`
+ Template types.Bool `tfsdk:"template"`
+ Timeouts timeouts.Value `tfsdk:"timeouts"`
+}
+
+// read retrieves the current state of the resource from the API and updates the state.
+// Returns false if the resource does not exist, so the caller can remove it from the state if necessary.
+func read(ctx context.Context, client proxmox.Client, model *Model, diags *diag.Diagnostics) bool {
+ vmAPI := client.Node(model.NodeName.ValueString()).VM(int(model.ID.ValueInt64()))
+
+ // Retrieve the entire configuration in order to compare it to the state.
+ config, err := vmAPI.GetVM(ctx)
+ if err != nil {
+ if errors.Is(err, api.ErrResourceDoesNotExist) {
+ tflog.Info(ctx, "VM does not exist, removing from the state", map[string]interface{}{
+ "vm_id": vmAPI.VMID,
+ })
+ } else {
+ diags.AddError("Failed to get VM", err.Error())
+ }
+
+ return false
+ }
+
+ status, err := vmAPI.GetVMStatus(ctx)
+ if err != nil {
+ diags.AddError("Failed to get VM status", err.Error())
+ return false
+ }
+
+ if status.VMID == nil {
+ diags.AddError("VM ID is missing in status API response", "")
+ return false
+ }
+
+ model.ID = types.Int64Value(int64(*status.VMID))
+
+ // Optional fields can be removed from the model, use StringPointerValue to handle removal on nil
+ model.Description = types.StringPointerValue(config.Description)
+ model.Name = types.StringPointerValue(config.Name)
+ model.CPU = cpu.NewValue(ctx, config, diags)
+ model.Tags = stringset.NewValue(config.Tags, diags)
+ model.Template = types.BoolPointerValue(config.Template.PointerBool())
+
+ return true
+}
diff --git a/templates/data-sources/virtual_environment_vm2.md.tmpl b/templates/data-sources/virtual_environment_vm2.md.tmpl
new file mode 100644
index 00000000..2038aa19
--- /dev/null
+++ b/templates/data-sources/virtual_environment_vm2.md.tmpl
@@ -0,0 +1,21 @@
+---
+layout: page
+title: {{.Name}}
+parent: Data Sources
+subcategory: Virtual Environment
+description: |-
+{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
+---
+
+# {{.Type}}: {{.Name}}
+
+!> **DO NOT USE**
+{{ .Description | trimspace }}
+
+{{ if .HasExample -}}
+## Example Usage
+
+{{ codefile "terraform" .ExampleFile }}
+{{- end }}
+
+{{ .SchemaMarkdown | trimspace }}
diff --git a/tools/tools.go b/tools/tools.go
index 4c689cee..e28391f8 100644
--- a/tools/tools.go
+++ b/tools/tools.go
@@ -29,7 +29,6 @@ import (
// Temporary: while migrating to the TF framework, we need to copy the generated docs to the right place
// for the resources / data sources that have been migrated.
//go:generate cp -R ../build/docs-gen/guides/ ../docs/guides/
-//go:generate cp ../build/docs-gen/data-sources/virtual_environment_version.md ../docs/data-sources/
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_hagroup.md ../docs/data-sources/
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_hagroups.md ../docs/data-sources/
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_hardware_mapping_pci.md ../docs/data-sources/
@@ -37,14 +36,16 @@ import (
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_hardware_mappings.md ../docs/data-sources/
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_haresource.md ../docs/data-sources/
//go:generate cp ../build/docs-gen/data-sources/virtual_environment_haresources.md ../docs/data-sources/
-//go:generate cp ../build/docs-gen/resources/virtual_environment_network_linux_bridge.md ../docs/resources/
-//go:generate cp ../build/docs-gen/resources/virtual_environment_network_linux_vlan.md ../docs/resources/
+//go:generate cp ../build/docs-gen/data-sources/virtual_environment_version.md ../docs/data-sources/
+//go:generate cp ../build/docs-gen/data-sources/virtual_environment_vm2.md ../docs/data-sources/
+//go:generate cp ../build/docs-gen/resources/virtual_environment_acl.md ../docs/resources/
+//go:generate cp ../build/docs-gen/resources/virtual_environment_cluster_options.md ../docs/resources/
+//go:generate cp ../build/docs-gen/resources/virtual_environment_download_file.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_hagroup.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_hardware_mapping_pci.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_hardware_mapping_usb.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_haresource.md ../docs/resources/
-//go:generate cp ../build/docs-gen/resources/virtual_environment_cluster_options.md ../docs/resources/
-//go:generate cp ../build/docs-gen/resources/virtual_environment_download_file.md ../docs/resources/
-//go:generate cp ../build/docs-gen/resources/virtual_environment_acl.md ../docs/resources/
+//go:generate cp ../build/docs-gen/resources/virtual_environment_network_linux_bridge.md ../docs/resources/
+//go:generate cp ../build/docs-gen/resources/virtual_environment_network_linux_vlan.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_user_token.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_vm2.md ../docs/resources/