mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 02:31:10 +00:00
feat(vm): add support for virtiofs
(#1900)
Signed-off-by: Fina Wilke <code@felinira.net> Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
ad41476962
commit
55b3f7391a
@ -80,6 +80,12 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
||||
}
|
||||
|
||||
serial_device {}
|
||||
|
||||
virtiofs {
|
||||
mapping = "data_share"
|
||||
cache = "always"
|
||||
direct_io = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "proxmox_virtual_environment_download_file" "latest_ubuntu_22_jammy_qcow2_img" {
|
||||
@ -559,6 +565,16 @@ output "ubuntu_vm_public_key" {
|
||||
- `virtio-gl` - VirtIO-GPU with 3D acceleration (VirGL). VirGL support needs some extra libraries that aren’t 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.
|
||||
- `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.
|
||||
- `virtiofs` - (Optional) Virtiofs share
|
||||
- `mapping` - Identifier of the directory mapping
|
||||
- `cache` - (Optional) The caching mode
|
||||
- `auto`
|
||||
- `always`
|
||||
- `metadata`
|
||||
- `never`
|
||||
- `direct_io` - (Optional) Whether to allow direct io
|
||||
- `expose_acl` - (Optional) Enable POSIX ACLs, implies xattr support
|
||||
- `expose_xattr` - (Optional) Enable support for extended attributes
|
||||
- `vm_id` - (Optional) The VM identifier.
|
||||
- `hook_script_file_id` - (Optional) The identifier for a file containing a hook script (needs to be executable, e.g. by using the `proxmox_virtual_environment_file.file_mode` attribute).
|
||||
- `watchdog` - (Optional) The watchdog configuration. Once enabled (by a guest action), the watchdog must be periodically polled by an agent inside the guest or else the watchdog will reset the guest (or execute the respective action specified).
|
||||
|
@ -396,6 +396,51 @@ func TestAccResourceVM(t *testing.T) {
|
||||
),
|
||||
},
|
||||
}},
|
||||
// Depends on #1902
|
||||
// {"create virtiofs block", []resource.TestStep{
|
||||
// {
|
||||
// Config: te.RenderConfig(`
|
||||
// resource "proxmox_virtual_environment_hardware_mapping_dir" "test" {
|
||||
// name = "test"
|
||||
|
||||
// map {
|
||||
// node = "{{.NodeName}}"
|
||||
// path = "/mnt"
|
||||
// }
|
||||
// }`, WithRootUser()),
|
||||
// Check: resource.ComposeTestCheckFunc(
|
||||
// ResourceAttributes("proxmox_virtual_environment_hardware_mapping_dir.test", map[string]string{
|
||||
// "name": "test",
|
||||
// "map.0.node": "{{.NodeName}}",
|
||||
// "map.0.path": "/mnt",
|
||||
// }),
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// Config: te.RenderConfig(`
|
||||
// resource "proxmox_virtual_environment_vm" "test_vm" {
|
||||
// node_name = "{{.NodeName}}"
|
||||
// started = false
|
||||
|
||||
// virtiofs {
|
||||
// mapping = "test"
|
||||
// cache = "always"
|
||||
// direct_io = true
|
||||
// expose_acl = false
|
||||
// expose_xattr = false
|
||||
// }
|
||||
// }`, WithRootUser()),
|
||||
// Check: resource.ComposeTestCheckFunc(
|
||||
// ResourceAttributes("proxmox_virtual_environment_vm.test_vm", map[string]string{
|
||||
// "virtiofs.0.mapping": "test",
|
||||
// "virtiofs.0.cache": "always",
|
||||
// "virtiofs.0.direct_io": "true",
|
||||
// "virtiofs.0.expose_acl": "false",
|
||||
// "virtiofs.0.expose_xattr": "false",
|
||||
// }),
|
||||
// ),
|
||||
// },
|
||||
// }},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
131
proxmox/nodes/vms/custom_virtiofs_share.go
Normal file
131
proxmox/nodes/vms/custom_virtiofs_share.go
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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 vms
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
||||
)
|
||||
|
||||
// CustomVirtiofsShare handles Virtiofs directory shares.
|
||||
type CustomVirtiofsShare struct {
|
||||
DirId string `json:"dirid" url:"dirid"`
|
||||
Cache *string `json:"cache,omitempty" url:"cache,omitempty"`
|
||||
DirectIo *types.CustomBool `json:"direct-io,omitempty" url:"direct-io,omitempty,int"`
|
||||
ExposeAcl *types.CustomBool `json:"expose-acl,omitempty" url:"expose-acl,omitempty,int"`
|
||||
ExposeXattr *types.CustomBool `json:"expose-xattr,omitempty" url:"expose-xattr,omitempty,int"`
|
||||
}
|
||||
|
||||
// CustomVirtiofsShares handles Virtiofs directory shares.
|
||||
type CustomVirtiofsShares map[string]*CustomVirtiofsShare
|
||||
|
||||
// EncodeValues converts a CustomVirtiofsShare struct to a URL value.
|
||||
func (r *CustomVirtiofsShare) EncodeValues(key string, v *url.Values) error {
|
||||
if r.ExposeAcl != nil && *r.ExposeAcl && r.ExposeXattr != nil && !*r.ExposeXattr {
|
||||
// expose-xattr implies expose-acl
|
||||
return errors.New("expose_xattr must be omitted or true when expose_acl is enabled")
|
||||
}
|
||||
|
||||
var values []string
|
||||
values = append(values, fmt.Sprintf("dirid=%s", r.DirId))
|
||||
|
||||
if r.Cache != nil {
|
||||
values = append(values, fmt.Sprintf("cache=%s", *r.Cache))
|
||||
}
|
||||
|
||||
if r.DirectIo != nil {
|
||||
if *r.DirectIo {
|
||||
values = append(values, "direct-io=1")
|
||||
} else {
|
||||
values = append(values, "direct-io=0")
|
||||
}
|
||||
}
|
||||
|
||||
if r.ExposeAcl != nil {
|
||||
if *r.ExposeAcl {
|
||||
values = append(values, "expose-acl=1")
|
||||
} else {
|
||||
values = append(values, "expose-acl=0")
|
||||
}
|
||||
}
|
||||
|
||||
if r.ExposeXattr != nil && (r.ExposeAcl == nil || !*r.ExposeAcl) {
|
||||
// expose-acl implies expose-xattr, omit it when unnecessary for consistency
|
||||
if *r.ExposeXattr {
|
||||
values = append(values, "expose-xattr=1")
|
||||
} else {
|
||||
values = append(values, "expose-xattr=0")
|
||||
}
|
||||
}
|
||||
|
||||
v.Add(key, strings.Join(values, ","))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeValues converts a CustomVirtiofsShares dict to multiple URL values.
|
||||
func (r CustomVirtiofsShares) EncodeValues(key string, v *url.Values) error {
|
||||
for s, d := range r {
|
||||
if err := d.EncodeValues(s, v); err != nil {
|
||||
return fmt.Errorf("failed to encode virtiofs share %s: %w", s, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON converts a CustomVirtiofsShare string to an object.
|
||||
func (r *CustomVirtiofsShare) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal CustomVirtiofsShare: %w", err)
|
||||
}
|
||||
|
||||
pairs := strings.Split(s, ",")
|
||||
|
||||
for _, p := range pairs {
|
||||
v := strings.Split(strings.TrimSpace(p), "=")
|
||||
|
||||
if len(v) == 1 {
|
||||
r.DirId = v[0]
|
||||
} else if len(v) == 2 {
|
||||
switch v[0] {
|
||||
case "dirid":
|
||||
r.DirId = v[1]
|
||||
case "cache":
|
||||
r.Cache = &v[1]
|
||||
case "direct-io":
|
||||
bv := types.CustomBool(v[1] == "1")
|
||||
r.DirectIo = &bv
|
||||
case "expose-acl":
|
||||
bv := types.CustomBool(v[1] == "1")
|
||||
r.ExposeAcl = &bv
|
||||
case "expose-xattr":
|
||||
bv := types.CustomBool(v[1] == "1")
|
||||
r.ExposeXattr = &bv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// expose-acl implies expose-xattr
|
||||
if r.ExposeAcl != nil && *r.ExposeAcl {
|
||||
if r.ExposeXattr == nil {
|
||||
bv := types.CustomBool(true)
|
||||
r.ExposeAcl = &bv
|
||||
} else if !*r.ExposeXattr {
|
||||
return fmt.Errorf("failed to unmarshal CustomVirtiofsShare: expose-xattr contradicts the value of expose-acl")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
79
proxmox/nodes/vms/custom_virtiofs_share_test.go
Normal file
79
proxmox/nodes/vms/custom_virtiofs_share_test.go
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 vms
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr"
|
||||
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
||||
)
|
||||
|
||||
func TestCustomVirtiofsShare_UnmarshalJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
line string
|
||||
want *CustomVirtiofsShare
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "id only virtiofs share",
|
||||
line: `"test"`,
|
||||
want: &CustomVirtiofsShare{
|
||||
DirId: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "virtiofs share with more details",
|
||||
line: `"folder,cache=always"`,
|
||||
want: &CustomVirtiofsShare{
|
||||
DirId: "folder",
|
||||
Cache: ptr.Ptr("always"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "virtiofs share with flags",
|
||||
line: `"folder,cache=never,direct-io=1,expose-acl=1"`,
|
||||
want: &CustomVirtiofsShare{
|
||||
DirId: "folder",
|
||||
Cache: ptr.Ptr("never"),
|
||||
DirectIo: types.CustomBool(true).Pointer(),
|
||||
ExposeAcl: types.CustomBool(true).Pointer(),
|
||||
ExposeXattr: types.CustomBool(true).Pointer(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "virtiofs share with xattr",
|
||||
line: `"folder,expose-xattr=1"`,
|
||||
want: &CustomVirtiofsShare{
|
||||
DirId: "folder",
|
||||
Cache: nil,
|
||||
DirectIo: types.CustomBool(false).Pointer(),
|
||||
ExposeAcl: types.CustomBool(false).Pointer(),
|
||||
ExposeXattr: types.CustomBool(true).Pointer(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "virtiofs share invalid combination",
|
||||
line: `"folder,expose-acl=1,expose-xattr=0"`,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := &CustomVirtiofsShare{}
|
||||
if err := r.UnmarshalJSON([]byte(tt.line)); (err != nil) != tt.wantErr {
|
||||
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -98,6 +98,7 @@ type CreateRequestBody struct {
|
||||
USBDevices CustomUSBDevices `json:"usb,omitempty" url:"usb,omitempty"`
|
||||
VGADevice *CustomVGADevice `json:"vga,omitempty" url:"vga,omitempty"`
|
||||
VirtualCPUCount *int64 `json:"vcpus,omitempty" url:"vcpus,omitempty"`
|
||||
VirtiofsShares CustomVirtiofsShares `json:"virtiofs,omitempty" url:"virtiofs,omitempty"`
|
||||
VMGenerationID *string `json:"vmgenid,omitempty" url:"vmgenid,omitempty"`
|
||||
VMID int `json:"vmid,omitempty" url:"vmid,omitempty"`
|
||||
VMStateDatastoreID *string `json:"vmstatestorage,omitempty" url:"vmstatestorage,omitempty"`
|
||||
@ -320,6 +321,7 @@ type GetResponseData struct {
|
||||
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty"`
|
||||
StorageDevices CustomStorageDevices `json:"-"`
|
||||
PCIDevices CustomPCIDevices `json:"-"`
|
||||
VirtiofsShares CustomVirtiofsShares `json:"-"`
|
||||
}
|
||||
|
||||
// GetStatusResponseBody contains the body from a VM get status response.
|
||||
@ -469,6 +471,7 @@ func (d *GetResponseData) UnmarshalJSON(b []byte) error {
|
||||
|
||||
data.StorageDevices = make(CustomStorageDevices)
|
||||
data.PCIDevices = make(CustomPCIDevices)
|
||||
data.VirtiofsShares = make(CustomVirtiofsShares)
|
||||
|
||||
for key, value := range byAttr {
|
||||
for _, prefix := range StorageInterfaces {
|
||||
@ -493,6 +496,15 @@ func (d *GetResponseData) UnmarshalJSON(b []byte) error {
|
||||
|
||||
data.PCIDevices[key] = &device
|
||||
}
|
||||
|
||||
if r := regexp.MustCompile(`^virtiofs\d+$`); r.MatchString(key) {
|
||||
var share CustomVirtiofsShare
|
||||
if err := json.Unmarshal([]byte(`"`+value.(string)+`"`), &share); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal %s: %w", key, err)
|
||||
}
|
||||
|
||||
data.VirtiofsShares[key] = &share
|
||||
}
|
||||
}
|
||||
|
||||
*d = GetResponseData(data)
|
||||
|
@ -28,7 +28,8 @@ func TestUnmarshalGetResponseData(t *testing.T) {
|
||||
"scsi22": "%[1]s",
|
||||
"hostpci0": "0000:81:00.2",
|
||||
"hostpci1": "host=81:00.4,pcie=0,rombar=1,x-vga=0",
|
||||
"hostpci12": "mapping=mappeddevice,pcie=0,rombar=1,x-vga=0"
|
||||
"hostpci12": "mapping=mappeddevice,pcie=0,rombar=1,x-vga=0",
|
||||
"virtiofs0":"test,cache=always,direct-io=1,expose-acl=1"
|
||||
}`, "local-lvm:vm-100-disk-0,aio=io_uring,backup=1,cache=none,discard=ignore,replicate=1,size=8G,ssd=1")
|
||||
|
||||
var data GetResponseData
|
||||
@ -57,6 +58,10 @@ func TestUnmarshalGetResponseData(t *testing.T) {
|
||||
assert.NotNil(t, data.PCIDevices["hostpci0"])
|
||||
assert.NotNil(t, data.PCIDevices["hostpci1"])
|
||||
assert.NotNil(t, data.PCIDevices["hostpci12"])
|
||||
|
||||
assert.NotNil(t, data.VirtiofsShares)
|
||||
assert.Len(t, data.VirtiofsShares, 1)
|
||||
assert.Equal(t, "always", *data.VirtiofsShares["virtiofs0"].Cache)
|
||||
}
|
||||
|
||||
func assertDevice(t *testing.T, dev *CustomStorageDevice) {
|
||||
|
@ -268,6 +268,16 @@ func IDEInterfaceValidator() schema.SchemaValidateDiagFunc {
|
||||
}, false))
|
||||
}
|
||||
|
||||
// VirtiofsCacheValidator is a schema validation function for virtiofs cache configs.
|
||||
func VirtiofsCacheValidator() schema.SchemaValidateDiagFunc {
|
||||
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
||||
"auto",
|
||||
"always",
|
||||
"metadata",
|
||||
"never",
|
||||
}, false))
|
||||
}
|
||||
|
||||
// CloudInitInterfaceValidator is a schema validation function that accepts either an IDE interface identifier or an
|
||||
// empty string, which is used as the default and means "detect which interface should be used automatically".
|
||||
func CloudInitInterfaceValidator() schema.SchemaValidateDiagFunc {
|
||||
|
@ -129,6 +129,10 @@ const (
|
||||
dvVGAClipboard = ""
|
||||
dvVGAMemory = 16
|
||||
dvVGAType = "std"
|
||||
dvVirtiofsCache = "auto"
|
||||
dvVirtiofsDirectIo = false
|
||||
dvVirtiofsExposeAcl = false
|
||||
dvVirtiofsExposeXattr = false
|
||||
dvSCSIHardware = "virtio-scsi-pci"
|
||||
dvStopOnDestroy = false
|
||||
dvHookScript = ""
|
||||
@ -142,6 +146,7 @@ const (
|
||||
maxResourceVirtualEnvironmentVMHostUSBDevices = 4
|
||||
// hardcoded /usr/share/perl5/PVE/QemuServer/Memory.pm: "our $MAX_NUMA = 8".
|
||||
maxResourceVirtualEnvironmentVMNUMADevices = 8
|
||||
maxResourceVirtualEnvironmentVirtiofsShares = 8
|
||||
|
||||
mkRebootAfterCreation = "reboot"
|
||||
mkRebootAfterUpdate = "reboot_after_update"
|
||||
@ -286,6 +291,12 @@ const (
|
||||
mkSCSIHardware = "scsi_hardware"
|
||||
mkHookScriptFileID = "hook_script_file_id"
|
||||
mkStopOnDestroy = "stop_on_destroy"
|
||||
mkVirtiofs = "virtiofs"
|
||||
mkVirtiofsMapping = "mapping"
|
||||
mkVirtiofsCache = "cache"
|
||||
mkVirtiofsDirectIo = "direct_io"
|
||||
mkVirtiofsExposeAcl = "expose_acl"
|
||||
mkVirtiofsExposeXattr = "expose_xattr"
|
||||
mkWatchdog = "watchdog"
|
||||
// a workaround for the lack of proper support of default and undefined values in SDK.
|
||||
mkWatchdogEnabled = "enabled"
|
||||
@ -1465,6 +1476,51 @@ func VM() *schema.Resource {
|
||||
MaxItems: 1,
|
||||
MinItems: 0,
|
||||
},
|
||||
mkVirtiofs: {
|
||||
Type: schema.TypeList,
|
||||
Description: "Virtiofs share configuration",
|
||||
Optional: true,
|
||||
DefaultFunc: func() (interface{}, error) {
|
||||
return []interface{}{}, nil
|
||||
},
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
mkVirtiofsMapping: {
|
||||
Type: schema.TypeString,
|
||||
Description: "Directory mapping identifier",
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
mkVirtiofsCache: {
|
||||
Type: schema.TypeString,
|
||||
Description: "The caching mode",
|
||||
Optional: true,
|
||||
Default: dvVirtiofsCache,
|
||||
ValidateDiagFunc: VirtiofsCacheValidator(),
|
||||
},
|
||||
mkVirtiofsDirectIo: {
|
||||
Type: schema.TypeBool,
|
||||
Description: "Whether to allow direct io",
|
||||
Optional: true,
|
||||
Default: dvVirtiofsDirectIo,
|
||||
},
|
||||
mkVirtiofsExposeAcl: {
|
||||
Type: schema.TypeBool,
|
||||
Description: "Enable POSIX ACLs, implies xattr support",
|
||||
Optional: true,
|
||||
Default: dvVirtiofsExposeAcl,
|
||||
},
|
||||
mkVirtiofsExposeXattr: {
|
||||
Type: schema.TypeBool,
|
||||
Description: "Enable support for extended attributes",
|
||||
Optional: true,
|
||||
Default: dvVirtiofsExposeXattr,
|
||||
},
|
||||
},
|
||||
},
|
||||
MaxItems: maxResourceVirtualEnvironmentVirtiofsShares,
|
||||
MinItems: 0,
|
||||
},
|
||||
mkVMID: {
|
||||
Type: schema.TypeInt,
|
||||
Description: "The VM identifier",
|
||||
@ -1899,6 +1955,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
||||
tabletDevice := types.CustomBool(d.Get(mkTabletDevice).(bool))
|
||||
template := types.CustomBool(d.Get(mkTemplate).(bool))
|
||||
vga := d.Get(mkVGA).([]interface{})
|
||||
virtiofs := d.Get(mkVirtiofs).([]interface{})
|
||||
watchdog := d.Get(mkWatchdog).([]interface{})
|
||||
|
||||
updateBody := &vms.UpdateRequestBody{
|
||||
@ -2155,6 +2212,11 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
||||
updateBody.VGADevice = vgaDevice
|
||||
}
|
||||
|
||||
if len(virtiofs) > 0 {
|
||||
virtiofsShares := vmGetVirtiofsShares(d)
|
||||
updateBody.VirtiofsShares = virtiofsShares
|
||||
}
|
||||
|
||||
hookScript := d.Get(mkHookScriptFileID).(string)
|
||||
currentHookScript := vmConfig.HookScript
|
||||
|
||||
@ -2535,6 +2597,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
||||
tabletDevice := types.CustomBool(d.Get(mkTabletDevice).(bool))
|
||||
template := types.CustomBool(d.Get(mkTemplate).(bool))
|
||||
|
||||
virtiofsShares := vmGetVirtiofsShares(d)
|
||||
vgaDevice := vmGetVGADeviceObject(d)
|
||||
|
||||
vmIDUntyped, hasVMID := d.GetOk(mkVMID)
|
||||
@ -2693,6 +2756,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
||||
TabletDeviceEnabled: &tabletDevice,
|
||||
Template: &template,
|
||||
USBDevices: usbDeviceObjects,
|
||||
VirtiofsShares: virtiofsShares,
|
||||
VGADevice: vgaDevice,
|
||||
VMID: vmID,
|
||||
WatchdogDevice: watchdogObject,
|
||||
@ -3347,6 +3411,41 @@ func vmGetTagsString(d *schema.ResourceData) string {
|
||||
return strings.Join(sanitizedTags, ";")
|
||||
}
|
||||
|
||||
func vmGetVirtiofsShares(d *schema.ResourceData) vms.CustomVirtiofsShares {
|
||||
virtiofs := d.Get(mkVirtiofs).([]interface{})
|
||||
virtiofsShares := make(vms.CustomVirtiofsShares, len(virtiofs))
|
||||
|
||||
for i, virtiofsShare := range virtiofs {
|
||||
block := virtiofsShare.(map[string]interface{})
|
||||
|
||||
mapping, _ := block[mkVirtiofsMapping].(string)
|
||||
cache, _ := block[mkVirtiofsCache].(string)
|
||||
direct_io := types.CustomBool(block[mkVirtiofsDirectIo].(bool))
|
||||
expose_acl := types.CustomBool(block[mkVirtiofsExposeAcl].(bool))
|
||||
expose_xattr := types.CustomBool(block[mkVirtiofsExposeXattr].(bool))
|
||||
|
||||
share := vms.CustomVirtiofsShare{
|
||||
DirId: mapping,
|
||||
DirectIo: &direct_io,
|
||||
ExposeAcl: &expose_acl,
|
||||
ExposeXattr: &expose_xattr,
|
||||
}
|
||||
|
||||
if cache != "" {
|
||||
share.Cache = &cache
|
||||
}
|
||||
|
||||
if share.ExposeAcl != nil && *share.ExposeAcl && share.ExposeXattr == nil {
|
||||
bv := types.CustomBool(true)
|
||||
share.ExposeXattr = &bv
|
||||
}
|
||||
|
||||
virtiofsShares[fmt.Sprintf("virtiofs%d", i)] = &share
|
||||
}
|
||||
|
||||
return virtiofsShares
|
||||
}
|
||||
|
||||
func vmGetVGADeviceObject(d *schema.ResourceData) *vms.CustomVGADevice {
|
||||
vga := d.Get(mkVGA).([]interface{})
|
||||
if len(vga) > 0 && vga[0] != nil {
|
||||
@ -3946,6 +4045,55 @@ func vmReadCustom(
|
||||
diags = append(diags, diag.FromErr(err)...)
|
||||
}
|
||||
|
||||
currentVirtiofsList := d.Get(mkVirtiofs).([]interface{})
|
||||
virtiofsMap := map[string]interface{}{}
|
||||
|
||||
for pi, pp := range vmConfig.VirtiofsShares {
|
||||
if pp == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
share := map[string]interface{}{}
|
||||
|
||||
share[mkVirtiofsMapping] = pp.DirId
|
||||
|
||||
if pp.Cache != nil {
|
||||
share[mkVirtiofsCache] = *pp.Cache
|
||||
} else {
|
||||
share[mkVirtiofsCache] = dvVirtiofsCache
|
||||
}
|
||||
|
||||
if pp.DirectIo != nil {
|
||||
share[mkVirtiofsDirectIo] = *pp.DirectIo
|
||||
} else {
|
||||
share[mkVirtiofsDirectIo] = dvVirtiofsDirectIo
|
||||
}
|
||||
|
||||
if pp.ExposeAcl != nil {
|
||||
share[mkVirtiofsExposeAcl] = *pp.ExposeAcl
|
||||
} else {
|
||||
share[mkVirtiofsExposeAcl] = dvVirtiofsExposeAcl
|
||||
}
|
||||
|
||||
switch {
|
||||
case pp.ExposeXattr != nil:
|
||||
share[mkVirtiofsExposeXattr] = *pp.ExposeXattr
|
||||
case pp.ExposeAcl != nil && bool(*pp.ExposeAcl):
|
||||
// expose-xattr implies expose-acl
|
||||
share[mkVirtiofsExposeXattr] = true
|
||||
default:
|
||||
share[mkVirtiofsExposeXattr] = dvVirtiofsExposeXattr
|
||||
}
|
||||
|
||||
virtiofsMap[pi] = share
|
||||
}
|
||||
|
||||
if len(clone) == 0 || len(currentVirtiofsList) > 0 {
|
||||
orderedVirtiofsList := utils.OrderedListFromMap(virtiofsMap)
|
||||
err := d.Set(mkVirtiofs, orderedVirtiofsList)
|
||||
diags = append(diags, diag.FromErr(err)...)
|
||||
}
|
||||
|
||||
// Compare the initialization configuration to the one stored in the state.
|
||||
initialization := map[string]interface{}{}
|
||||
|
||||
@ -5339,6 +5487,17 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
||||
rebootRequired = true
|
||||
}
|
||||
|
||||
// Prepare the new Virtiofs shares configuration.
|
||||
if d.HasChange(mkVirtiofs) {
|
||||
updateBody.VirtiofsShares = vmGetVirtiofsShares(d)
|
||||
|
||||
for i := len(updateBody.VirtiofsShares); i < maxResourceVirtualEnvironmentVirtiofsShares; i++ {
|
||||
del = append(del, fmt.Sprintf("virtiofs%d", i))
|
||||
}
|
||||
|
||||
rebootRequired = true
|
||||
}
|
||||
|
||||
// Prepare the new SCSI hardware type
|
||||
if d.HasChange(mkSCSIHardware) {
|
||||
scsiHardware := d.Get(mkSCSIHardware).(string)
|
||||
|
@ -64,6 +64,7 @@ func TestVMSchema(t *testing.T) {
|
||||
mkStarted,
|
||||
mkTabletDevice,
|
||||
mkTemplate,
|
||||
mkVirtiofs,
|
||||
mkVMID,
|
||||
mkSCSIHardware,
|
||||
})
|
||||
@ -93,6 +94,7 @@ func TestVMSchema(t *testing.T) {
|
||||
mkStarted: schema.TypeBool,
|
||||
mkTabletDevice: schema.TypeBool,
|
||||
mkTemplate: schema.TypeBool,
|
||||
mkVirtiofs: schema.TypeList,
|
||||
mkVMID: schema.TypeInt,
|
||||
mkSCSIHardware: schema.TypeString,
|
||||
})
|
||||
@ -382,6 +384,23 @@ func TestVMSchema(t *testing.T) {
|
||||
mkSerialDeviceDevice: schema.TypeString,
|
||||
})
|
||||
|
||||
virtiofsSchema := test.AssertNestedSchemaExistence(t, s, mkVirtiofs)
|
||||
|
||||
test.AssertOptionalArguments(t, virtiofsSchema, []string{
|
||||
mkVirtiofsCache,
|
||||
mkVirtiofsDirectIo,
|
||||
mkVirtiofsExposeAcl,
|
||||
mkVirtiofsExposeXattr,
|
||||
})
|
||||
|
||||
test.AssertValueTypes(t, virtiofsSchema, map[string]schema.ValueType{
|
||||
mkVirtiofsMapping: schema.TypeString,
|
||||
mkVirtiofsCache: schema.TypeString,
|
||||
mkVirtiofsDirectIo: schema.TypeBool,
|
||||
mkVirtiofsExposeAcl: schema.TypeBool,
|
||||
mkVirtiofsExposeXattr: schema.TypeBool,
|
||||
})
|
||||
|
||||
vgaSchema := test.AssertNestedSchemaExistence(t, s, mkVGA)
|
||||
|
||||
test.AssertOptionalArguments(t, vgaSchema, []string{
|
||||
|
Loading…
Reference in New Issue
Block a user