From c2d3f46474fc0d0603c34596eb81b82c06713b17 Mon Sep 17 00:00:00 2001 From: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Date: Tue, 21 Mar 2023 21:52:58 -0400 Subject: [PATCH] feat(vm): Add bare minimum VM datasource (#268) * feat(vm): Add a bare minimum VM datasource * fix linter errors * fix doc * add lookup across all nodes in the cluster, add filter by tags * stable vm list * fix linter errors * pr feedback: switch to dynamic id for vms * add datasource examples * add unit tests * Update virtual_environment_vms.md --- docs/_config.yml | 2 +- docs/data-sources/virtual_environment_vm.md | 31 ++++ docs/data-sources/virtual_environment_vms.md | 34 ++++ example/data_source_virtual_environment_vm.tf | 9 + .../data_source_virtual_environment_vms.tf | 15 ++ go.mod | 2 + go.sum | 4 + proxmox/virtual_environment_vm.go | 23 ++- proxmox/virtual_environment_vm_types.go | 4 +- .../data_source_virtual_environment_vm.go | 101 ++++++++++++ ...data_source_virtual_environment_vm_test.go | 41 +++++ .../data_source_virtual_environment_vms.go | 154 ++++++++++++++++++ ...ata_source_virtual_environment_vms_test.go | 53 ++++++ proxmoxtf/provider.go | 2 + 14 files changed, 471 insertions(+), 4 deletions(-) create mode 100644 docs/data-sources/virtual_environment_vm.md create mode 100644 docs/data-sources/virtual_environment_vms.md create mode 100644 example/data_source_virtual_environment_vm.tf create mode 100644 example/data_source_virtual_environment_vms.tf create mode 100644 proxmoxtf/data_source_virtual_environment_vm.go create mode 100644 proxmoxtf/data_source_virtual_environment_vm_test.go create mode 100644 proxmoxtf/data_source_virtual_environment_vms.go create mode 100644 proxmoxtf/data_source_virtual_environment_vms_test.go diff --git a/docs/_config.yml b/docs/_config.yml index 14a7ca67..9138c05b 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -11,6 +11,6 @@ plugins: remote_theme: pmarsceill/just-the-docs # Theme settings. -footer_content: 'Copyright © 2019-2021 Danitso - Distributed under the Mozilla Public License 2.0
Copyright © 2021-2022 Pavel Boldyrev - Distributed under the Mozilla Public License 2.0' +footer_content: 'Copyright © 2019-2021 Danitso - Distributed under the Mozilla Public License 2.0
Copyright © 2021-2023 Pavel Boldyrev - Distributed under the Mozilla Public License 2.0' heading_anchors: true search_enabled: false diff --git a/docs/data-sources/virtual_environment_vm.md b/docs/data-sources/virtual_environment_vm.md new file mode 100644 index 00000000..90b68c7f --- /dev/null +++ b/docs/data-sources/virtual_environment_vm.md @@ -0,0 +1,31 @@ +--- +layout: page +title: proxmox_virtual_environment_vm +permalink: /data-sources/virtual_environment_vm +nav_order: 17 +parent: Data Sources +subcategory: Virtual Environment +--- + +# Data Source: proxmox_virtual_environment_vm + +Retrieves information about a specific VM. + +## Example Usage + +```terraform +data "proxmox_virtual_environment_vm" "test_vm" { + node_name = "test" + vm_id = 100 +} +``` + +## Argument Reference + +- `node_name` - (Required) The node name. +- `vm_id` - (Required) The VM identifier. + +## Attribute Reference + +- `name` - The virtual machine name. +- `tags` - A list of tags of the VM. diff --git a/docs/data-sources/virtual_environment_vms.md b/docs/data-sources/virtual_environment_vms.md new file mode 100644 index 00000000..1cd9fae3 --- /dev/null +++ b/docs/data-sources/virtual_environment_vms.md @@ -0,0 +1,34 @@ +--- +layout: page +title: proxmox_virtual_environment_vms +permalink: /data-sources/virtual_environment_vms +nav_order: 18 +parent: Data Sources +subcategory: Virtual Environment +--- + +# Data Source: proxmox_virtual_environment_vms + +Retrieves information about all VMs in the Proxmox cluster. + +## Example Usage + +```terraform +data "proxmox_virtual_environment_vms" "ubuntu_vms" { + tags = ["ubuntu"] +} +``` + +## Argument Reference + +- `node_name` - (Optional) The node name. +- `tags` - (Optional) A list of tags to filter the VMs. The VM must have all + the tags to be included in the result. + +## Attribute Reference + +- `vms` - The VMs list. + - `name` - The virtual machine name. + - `node_name` - The node name. + - `tags` - A list of tags of the VM. + - `vm_id` - The VM identifier. diff --git a/example/data_source_virtual_environment_vm.tf b/example/data_source_virtual_environment_vm.tf new file mode 100644 index 00000000..d908770d --- /dev/null +++ b/example/data_source_virtual_environment_vm.tf @@ -0,0 +1,9 @@ +data "proxmox_virtual_environment_vm" "example" { + depends_on = [proxmox_virtual_environment_vm.example] + vm_id = proxmox_virtual_environment_vm.example.vm_id + node_name = data.proxmox_virtual_environment_nodes.example.names[0] +} + +output "proxmox_virtual_environment_vm_example" { + value = data.proxmox_virtual_environment_vm.example +} diff --git a/example/data_source_virtual_environment_vms.tf b/example/data_source_virtual_environment_vms.tf new file mode 100644 index 00000000..e137745b --- /dev/null +++ b/example/data_source_virtual_environment_vms.tf @@ -0,0 +1,15 @@ +data "proxmox_virtual_environment_vms" "example" { + depends_on = [proxmox_virtual_environment_vm.example] + tags = ["ubuntu"] + + lifecycle { + postcondition { + condition = length(self.vms) == 1 + error_message = "Only 1 vm should have this tag" + } + } +} + +output "proxmox_virtual_environment_vms_example" { + value = data.proxmox_virtual_environment_vms.example.vms +} diff --git a/go.mod b/go.mod index 14abb8e8..5c04116f 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/google/go-querystring v1.1.0 + github.com/google/uuid v1.3.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/terraform-plugin-log v0.8.0 @@ -12,6 +13,7 @@ require ( github.com/skeema/knownhosts v1.1.0 github.com/stretchr/testify v1.8.2 golang.org/x/crypto v0.7.0 + golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 ) require ( diff --git a/go.sum b/go.sum index 95bc104c..17d8d290 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -124,6 +126,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= +golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/proxmox/virtual_environment_vm.go b/proxmox/virtual_environment_vm.go index 39068a7f..79a22271 100644 --- a/proxmox/virtual_environment_vm.go +++ b/proxmox/virtual_environment_vm.go @@ -322,8 +322,27 @@ func (c *VirtualEnvironmentClient) MoveVMDiskAsync( } // ListVMs retrieves a list of virtual machines. -func (c *VirtualEnvironmentClient) ListVMs() ([]*VirtualEnvironmentVMListResponseData, error) { - return nil, errors.New("not implemented") +func (c *VirtualEnvironmentClient) ListVMs( + ctx context.Context, + nodeName string, +) ([]*VirtualEnvironmentVMListResponseData, error) { + resBody := &VirtualEnvironmentVMListResponseBody{} + err := c.DoRequest( + ctx, + hmGET, + fmt.Sprintf("nodes/%s/qemu", url.PathEscape(nodeName)), + nil, + resBody, + ) + if err != nil { + return nil, err + } + + if resBody.Data == nil { + return nil, errors.New("the server did not include a data object in the response") + } + + return resBody.Data, nil } // RebootVM reboots a virtual machine. diff --git a/proxmox/virtual_environment_vm_types.go b/proxmox/virtual_environment_vm_types.go index e5a3d97b..caedbfa1 100644 --- a/proxmox/virtual_environment_vm_types.go +++ b/proxmox/virtual_environment_vm_types.go @@ -504,7 +504,9 @@ type VirtualEnvironmentVMListResponseBody struct { // VirtualEnvironmentVMListResponseData contains the data from an virtual machine list response. type VirtualEnvironmentVMListResponseData struct { - ACPI *CustomBool `json:"acpi,omitempty" url:"acpi,omitempty,int"` + Name *string `json:"name,omitempty"` + Tags *string `json:"tags,omitempty"` + VMID int `json:"vmid,omitempty"` } // VirtualEnvironmentVMMigrateRequestBody contains the body for a VM migration request. diff --git a/proxmoxtf/data_source_virtual_environment_vm.go b/proxmoxtf/data_source_virtual_environment_vm.go new file mode 100644 index 00000000..9a78f9e9 --- /dev/null +++ b/proxmoxtf/data_source_virtual_environment_vm.go @@ -0,0 +1,101 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package proxmoxtf + +import ( + "context" + "sort" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + mkDataSourceVirtualEnvironmentVMName = "name" + mkDataSourceVirtualEnvironmentVMNodeName = "node_name" + mkDataSourceVirtualEnvironmentVMTags = "tags" + mkDataSourceVirtualEnvironmentVMVMID = "vm_id" +) + +func dataSourceVirtualEnvironmentVM() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkDataSourceVirtualEnvironmentVMName: { + Type: schema.TypeString, + Description: "The VM name", + Computed: true, + }, + mkDataSourceVirtualEnvironmentVMNodeName: { + Type: schema.TypeString, + Description: "The node name", + Required: true, + }, + mkDataSourceVirtualEnvironmentVMTags: { + Type: schema.TypeList, + Description: "Tags of the VM", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + mkDataSourceVirtualEnvironmentVMVMID: { + Type: schema.TypeInt, + Description: "The VM identifier", + Required: true, + }, + }, + ReadContext: dataSourceVirtualEnvironmentVMRead, + } +} + +// dataSourceVirtualEnvironmentVMRead reads the data of a VM by ID. +func dataSourceVirtualEnvironmentVMRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + if err != nil { + return diag.FromErr(err) + } + + nodeName := d.Get(mkDataSourceVirtualEnvironmentVMNodeName).(string) + vmID := d.Get(mkDataSourceVirtualEnvironmentVMVMID).(int) + + vmStatus, err := veClient.GetVMStatus(ctx, nodeName, vmID) + if err != nil { + if strings.Contains(err.Error(), "HTTP 404") || + (strings.Contains(err.Error(), "HTTP 500") && strings.Contains(err.Error(), "does not exist")) { + d.SetId("") + + return nil + } + + return diag.FromErr(err) + } + + if vmStatus.Name != nil { + err = d.Set(mkDataSourceVirtualEnvironmentVMName, *vmStatus.Name) + } else { + err = d.Set(mkDataSourceVirtualEnvironmentVMName, "") + } + diags = append(diags, diag.FromErr(err)...) + + var tags []string + if vmStatus.Tags != nil { + for _, tag := range strings.Split(*vmStatus.Tags, ";") { + t := strings.TrimSpace(tag) + if len(t) > 0 { + tags = append(tags, t) + } + } + sort.Strings(tags) + } + err = d.Set(mkDataSourceVirtualEnvironmentVMTags, tags) + diags = append(diags, diag.FromErr(err)...) + + d.SetId(strconv.Itoa(vmID)) + + return diags +} diff --git a/proxmoxtf/data_source_virtual_environment_vm_test.go b/proxmoxtf/data_source_virtual_environment_vm_test.go new file mode 100644 index 00000000..0cdaf726 --- /dev/null +++ b/proxmoxtf/data_source_virtual_environment_vm_test.go @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package proxmoxtf + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// TestDataSourceVirtualEnvironmentVMInstantiation tests whether the dataSourceVirtualEnvironmentVM instance can be instantiated. +func TestDataSourceVirtualEnvironmentVMInstantiation(t *testing.T) { + t.Parallel() + + s := dataSourceVirtualEnvironmentVM() + + if s == nil { + t.Fatalf("Cannot instantiate dataSourceVirtualEnvironmentVM") + } +} + +// TestDataSourceVirtualEnvironmentVMSchema tests the dataSourceVirtualEnvironmentVM schema. +func TestDataSourceVirtualEnvironmentVMSchema(t *testing.T) { + t.Parallel() + + s := dataSourceVirtualEnvironmentVM() + + testComputedAttributes(t, s, []string{ + mkDataSourceVirtualEnvironmentVMName, + mkDataSourceVirtualEnvironmentVMTags, + }) + + testValueTypes(t, s, map[string]schema.ValueType{ + mkDataSourceVirtualEnvironmentVMName: schema.TypeString, + mkDataSourceVirtualEnvironmentVMNodeName: schema.TypeString, + mkDataSourceVirtualEnvironmentVMTags: schema.TypeList, + mkDataSourceVirtualEnvironmentVMVMID: schema.TypeInt, + }) +} diff --git a/proxmoxtf/data_source_virtual_environment_vms.go b/proxmoxtf/data_source_virtual_environment_vms.go new file mode 100644 index 00000000..696c40d0 --- /dev/null +++ b/proxmoxtf/data_source_virtual_environment_vms.go @@ -0,0 +1,154 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package proxmoxtf + +import ( + "context" + "fmt" + "sort" + "strings" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "golang.org/x/exp/slices" + + "github.com/bpg/terraform-provider-proxmox/proxmox" +) + +const ( + mkDataSourceVirtualEnvironmentVMs = "vms" +) + +func dataSourceVirtualEnvironmentVMs() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkDataSourceVirtualEnvironmentVMNodeName: { + Type: schema.TypeString, + Optional: true, + Description: "The node name", + }, + mkDataSourceVirtualEnvironmentVMTags: { + Type: schema.TypeList, + Description: "Tags of the VM to match", + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + mkDataSourceVirtualEnvironmentVMs: { + Type: schema.TypeList, + Description: "VMs", + Computed: true, + Elem: &schema.Resource{ + Schema: dataSourceVirtualEnvironmentVM().Schema, + }, + }, + }, + ReadContext: dataSourceVirtualEnvironmentVMsRead, + } +} + +// dataSourceVirtualEnvironmentVMRead reads the data of a VM by ID. +func dataSourceVirtualEnvironmentVMsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + if err != nil { + return diag.FromErr(err) + } + + nodeNames, err := getNodeNames(ctx, d, veClient) + if err != nil { + return diag.FromErr(err) + } + + tagsData := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{}) + var filterTags []string + for i := 0; i < len(tagsData); i++ { + tag := strings.TrimSpace(tagsData[i].(string)) + if len(tag) > 0 { + filterTags = append(filterTags, tag) + } + } + sort.Strings(filterTags) + + var vms []interface{} + for _, nodeName := range nodeNames { + listData, err := veClient.ListVMs(ctx, nodeName) + if err != nil { + diags = append(diags, diag.FromErr(err)...) + } + + sort.Slice(listData, func(i, j int) bool { + return listData[i].VMID < listData[j].VMID + }) + + for _, data := range listData { + vm := map[string]interface{}{ + mkDataSourceVirtualEnvironmentVMNodeName: nodeName, + mkDataSourceVirtualEnvironmentVMVMID: data.VMID, + } + + if data.Name != nil { + vm[mkDataSourceVirtualEnvironmentVMName] = *data.Name + } else { + vm[mkDataSourceVirtualEnvironmentVMName] = "" + } + + var tags []string + if data.Tags != nil && *data.Tags != "" { + tags = strings.Split(*data.Tags, ";") + sort.Strings(tags) + vm[mkDataSourceVirtualEnvironmentVMTags] = tags + } + + if len(filterTags) > 0 { + match := true + for _, tag := range filterTags { + if !slices.Contains(tags, tag) { + match = false + break + } + } + if !match { + continue + } + } + + vms = append(vms, vm) + } + } + + err = d.Set(mkDataSourceVirtualEnvironmentVMs, vms) + diags = append(diags, diag.FromErr(err)...) + + d.SetId(uuid.New().String()) + + return diags +} + +func getNodeNames( + ctx context.Context, + d *schema.ResourceData, + veClient *proxmox.VirtualEnvironmentClient, +) ([]string, error) { + var nodeNames []string + nodeName := d.Get(mkDataSourceVirtualEnvironmentVMNodeName).(string) + if nodeName != "" { + nodeNames = append(nodeNames, nodeName) + } else { + nodes, err := veClient.ListNodes(ctx) + if err != nil { + return nil, fmt.Errorf("error listing nodes: %w", err) + } + + for _, node := range nodes { + nodeNames = append(nodeNames, node.Name) + } + } + + sort.Strings(nodeNames) + return nodeNames, nil +} diff --git a/proxmoxtf/data_source_virtual_environment_vms_test.go b/proxmoxtf/data_source_virtual_environment_vms_test.go new file mode 100644 index 00000000..e1732656 --- /dev/null +++ b/proxmoxtf/data_source_virtual_environment_vms_test.go @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package proxmoxtf + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// TestDataSourceVirtualEnvironmentVMsInstantiation tests whether the dataSourceVirtualEnvironmentVMs instance can be instantiated. +func TestDataSourceVirtualEnvironmentVMsInstantiation(t *testing.T) { + t.Parallel() + + s := dataSourceVirtualEnvironmentVMs() + + if s == nil { + t.Fatalf("Cannot instantiate dataSourceVirtualEnvironmentVMs") + } +} + +// TestDataSourceVirtualEnvironmentVMsSchema tests the dataSourceVirtualEnvironmentVMs schema. +func TestDataSourceVirtualEnvironmentVMsSchema(t *testing.T) { + t.Parallel() + + s := dataSourceVirtualEnvironmentVMs() + + testComputedAttributes(t, s, []string{ + mkDataSourceVirtualEnvironmentVMs, + }) + + testValueTypes(t, s, map[string]schema.ValueType{ + mkDataSourceVirtualEnvironmentVMNodeName: schema.TypeString, + mkDataSourceVirtualEnvironmentVMTags: schema.TypeList, + mkDataSourceVirtualEnvironmentVMs: schema.TypeList, + }) + + vmsSchema := testNestedSchemaExistence(t, s, mkDataSourceVirtualEnvironmentVMs) + + testComputedAttributes(t, vmsSchema, []string{ + mkDataSourceVirtualEnvironmentVMName, + mkDataSourceVirtualEnvironmentVMTags, + }) + + testValueTypes(t, vmsSchema, map[string]schema.ValueType{ + mkDataSourceVirtualEnvironmentVMName: schema.TypeString, + mkDataSourceVirtualEnvironmentVMNodeName: schema.TypeString, + mkDataSourceVirtualEnvironmentVMTags: schema.TypeList, + mkDataSourceVirtualEnvironmentVMVMID: schema.TypeInt, + }) +} diff --git a/proxmoxtf/provider.go b/proxmoxtf/provider.go index 3ca6538d..2868534e 100644 --- a/proxmoxtf/provider.go +++ b/proxmoxtf/provider.go @@ -55,6 +55,8 @@ func Provider() *schema.Provider { "proxmox_virtual_environment_user": dataSourceVirtualEnvironmentUser(), "proxmox_virtual_environment_users": dataSourceVirtualEnvironmentUsers(), "proxmox_virtual_environment_version": dataSourceVirtualEnvironmentVersion(), + "proxmox_virtual_environment_vm": dataSourceVirtualEnvironmentVM(), + "proxmox_virtual_environment_vms": dataSourceVirtualEnvironmentVMs(), }, ResourcesMap: map[string]*schema.Resource{ "proxmox_virtual_environment_certificate": resourceVirtualEnvironmentCertificate(),