mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-07-09 15:25:01 +00:00
feat(vm): Import Disk via API. (#2012)
* feat(vm): Import Disk via API. Signed-off-by: Marco Attia <54147992+Vaneixus@users.noreply.github.com> * lint(vm): fix Linter Issues. Signed-off-by: Marco Attia <54147992+Vaneixus@users.noreply.github.com> * fix(vm): import_from update issues. Signed-off-by: Marco Attia <54147992+Vaneixus@users.noreply.github.com> * fix: store `import_from` in the state, add acc test for `import_from` Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> * chore: update examples and docs Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> * fix: linter Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> * chore: re-gen docs Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> --------- Signed-off-by: Marco Attia <54147992+Vaneixus@users.noreply.github.com> 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
6a8f367c46
commit
ddc4118b08
@ -112,10 +112,6 @@ Add the following block to your VM config:
|
|||||||
|
|
||||||
For more context, see #1639 and #1770.
|
For more context, see #1639 and #1770.
|
||||||
|
|
||||||
### Disk Images Cannot Be Imported by Non-PAM Accounts
|
|
||||||
|
|
||||||
Due to limitations in the Proxmox VE API, certain actions need to be performed using SSH. This requires the use of a PAM account (standard Linux account).
|
|
||||||
|
|
||||||
### Disk Images from VMware Cannot Be Uploaded or Imported
|
### Disk Images from VMware Cannot Be Uploaded or Imported
|
||||||
|
|
||||||
Proxmox VE does not currently support VMware disk images directly.
|
Proxmox VE does not currently support VMware disk images directly.
|
||||||
|
@ -32,7 +32,7 @@ resource "proxmox_virtual_environment_vm" "centos_vm" {
|
|||||||
|
|
||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
file_id = proxmox_virtual_environment_download_file.centos_cloud_image.id
|
import_from = proxmox_virtual_environment_download_file.centos_cloud_image.id
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
discard = "on"
|
discard = "on"
|
||||||
@ -41,11 +41,10 @@ resource "proxmox_virtual_environment_vm" "centos_vm" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_download_file" "centos_cloud_image" {
|
resource "proxmox_virtual_environment_download_file" "centos_cloud_image" {
|
||||||
content_type = "iso"
|
content_type = "import"
|
||||||
datastore_id = "local"
|
datastore_id = "local"
|
||||||
node_name = "pve"
|
node_name = "pve"
|
||||||
url = "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-latest.x86_64.qcow2"
|
url = "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-latest.x86_64.qcow2"
|
||||||
file_name = "centos8.img"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -69,7 +68,7 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
|
|
||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
file_id = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
import_from = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
discard = "on"
|
discard = "on"
|
||||||
@ -78,10 +77,12 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
||||||
content_type = "iso"
|
content_type = "import"
|
||||||
datastore_id = "local"
|
datastore_id = "local"
|
||||||
node_name = "pve"
|
node_name = "pve"
|
||||||
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
||||||
|
# need to rename the file to *.qcow2 to indicate the actual file format for import
|
||||||
|
file_name = "jammy-server-cloudimg-amd64.qcow2"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -110,6 +111,7 @@ resource "proxmox_virtual_environment_vm" "debian_vm" {
|
|||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
# qcow2 image downloaded from https://cloud.debian.org/images/cloud/bookworm/latest/ and renamed to *.img
|
# qcow2 image downloaded from https://cloud.debian.org/images/cloud/bookworm/latest/ and renamed to *.img
|
||||||
|
# the image is not of import type, so provider will use SSH client to import it
|
||||||
file_id = "local:iso/debian-12-genericcloud-amd64.img"
|
file_id = "local:iso/debian-12-genericcloud-amd64.img"
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
|
@ -40,7 +40,7 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
|
|
||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
file_id = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
import_from = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
discard = "on"
|
discard = "on"
|
||||||
@ -53,11 +53,12 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
||||||
content_type = "iso"
|
content_type = "import"
|
||||||
datastore_id = "local"
|
datastore_id = "local"
|
||||||
node_name = "pve"
|
node_name = "pve"
|
||||||
|
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
||||||
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
# need to rename the file to *.qcow2 to indicate the actual file format for import
|
||||||
|
file_name = "jammy-server-cloudimg-amd64.qcow2"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -130,7 +131,7 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
|
|
||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
file_id = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
import_from = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
discard = "on"
|
discard = "on"
|
||||||
@ -154,11 +155,12 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
||||||
content_type = "iso"
|
content_type = "import"
|
||||||
datastore_id = "local"
|
datastore_id = "local"
|
||||||
node_name = "pve"
|
node_name = "pve"
|
||||||
|
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
||||||
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
# need to rename the file to *.qcow2 to indicate the actual file format for import
|
||||||
|
file_name = "jammy-server-cloudimg-amd64.qcow2"
|
||||||
}
|
}
|
||||||
|
|
||||||
output "vm_ipv4_address" {
|
output "vm_ipv4_address" {
|
||||||
|
@ -296,7 +296,9 @@ terraform plan
|
|||||||
|
|
||||||
The Proxmox provider can connect to a Proxmox node via SSH.
|
The Proxmox provider can connect to a Proxmox node via SSH.
|
||||||
This is used in the `proxmox_virtual_environment_vm` or `proxmox_virtual_environment_file` resource to execute commands on the node to perform actions that are not supported by Proxmox API.
|
This is used in the `proxmox_virtual_environment_vm` or `proxmox_virtual_environment_file` resource to execute commands on the node to perform actions that are not supported by Proxmox API.
|
||||||
For example, to import VM disks, or to uploading certain type of resources, such as snippets.
|
For example, to import VM disks in certain cases, or to uploading certain type of resources, such as snippets.
|
||||||
|
|
||||||
|
~> Note that the SSH connection is not used when VM disk is imported using `import_from` attribute. It also is not used to _manage_ VMs or Containers, and is not required for most operations.
|
||||||
|
|
||||||
The SSH connection configuration is provided via the optional `ssh` block in the `provider` block:
|
The SSH connection configuration is provided via the optional `ssh` block in the `provider` block:
|
||||||
|
|
||||||
|
@ -9,8 +9,6 @@ subcategory: Virtual Environment
|
|||||||
|
|
||||||
Manages a virtual machine.
|
Manages a virtual machine.
|
||||||
|
|
||||||
> This resource uses SSH access to the node. You might need to configure the [`ssh` option in the `provider` section](../index.md#node-ip-address-used-for-ssh-connection).
|
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
@ -47,7 +45,7 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
|
|
||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
file_id = proxmox_virtual_environment_download_file.latest_ubuntu_22_jammy_qcow2_img.id
|
import_from = proxmox_virtual_environment_download_file.latest_ubuntu_22_jammy_qcow2_img.id
|
||||||
interface = "scsi0"
|
interface = "scsi0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,10 +87,12 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_download_file" "latest_ubuntu_22_jammy_qcow2_img" {
|
resource "proxmox_virtual_environment_download_file" "latest_ubuntu_22_jammy_qcow2_img" {
|
||||||
content_type = "iso"
|
content_type = "import"
|
||||||
datastore_id = "local"
|
datastore_id = "local"
|
||||||
node_name = "pve"
|
node_name = "pve"
|
||||||
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
||||||
|
# need to rename the file to *.qcow2 to indicate the actual file format for import
|
||||||
|
file_name = "jammy-server-cloudimg-amd64.qcow2"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "random_password" "ubuntu_vm_password" {
|
resource "random_password" "ubuntu_vm_password" {
|
||||||
@ -295,7 +295,10 @@ output "ubuntu_vm_public_key" {
|
|||||||
- `vmdk` - VMware Disk Image.
|
- `vmdk` - VMware Disk Image.
|
||||||
- `file_id` - (Optional) The file ID for a disk image when importing a disk into VM. The ID format is
|
- `file_id` - (Optional) The file ID for a disk image when importing a disk into VM. The ID format is
|
||||||
`<datastore_id>:<content_type>/<file_name>`, for example `local:iso/centos8.img`. Can be also taken from
|
`<datastore_id>:<content_type>/<file_name>`, for example `local:iso/centos8.img`. Can be also taken from
|
||||||
`proxmox_virtual_environment_download_file` resource.
|
`proxmox_virtual_environment_download_file` resource. *Deprecated*, use `import_from` instead.
|
||||||
|
- `import_from` - (Optional) The file ID for a disk image to import into VM. The image must be of `import` content type.
|
||||||
|
The ID format is `<datastore_id>:import/<file_name>`, for example `local:import/centos8.qcow2`. Can be also taken from
|
||||||
|
`proxmox_virtual_environment_download_file` resource.
|
||||||
- `interface` - (Required) The disk interface for Proxmox, currently `scsi`,
|
- `interface` - (Required) The disk interface for Proxmox, currently `scsi`,
|
||||||
`sata` and `virtio` interfaces are supported. Append the disk index at
|
`sata` and `virtio` interfaces are supported. Append the disk index at
|
||||||
the end, for example, `virtio0` for the first virtio disk, `virtio1` for
|
the end, for example, `virtio0` for the first virtio disk, `virtio1` for
|
||||||
|
@ -231,11 +231,18 @@ resource "proxmox_virtual_environment_vm" "data_vm" {
|
|||||||
disk {
|
disk {
|
||||||
datastore_id = local.datastore_id
|
datastore_id = local.datastore_id
|
||||||
interface = "scsi0"
|
interface = "scsi0"
|
||||||
|
size = 8
|
||||||
|
import_from = proxmox_virtual_environment_download_file.latest_debian_12_bookworm_qcow2.id
|
||||||
|
}
|
||||||
|
|
||||||
|
disk {
|
||||||
|
datastore_id = local.datastore_id
|
||||||
|
interface = "scsi1"
|
||||||
size = 1
|
size = 1
|
||||||
}
|
}
|
||||||
disk {
|
disk {
|
||||||
datastore_id = local.datastore_id
|
datastore_id = local.datastore_id
|
||||||
interface = "scsi1"
|
interface = "scsi2"
|
||||||
size = 4
|
size = 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ resource "proxmox_virtual_environment_vm" "centos_vm" {
|
|||||||
|
|
||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
file_id = proxmox_virtual_environment_download_file.centos_cloud_image.id
|
import_from = proxmox_virtual_environment_download_file.centos_cloud_image.id
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
discard = "on"
|
discard = "on"
|
||||||
@ -24,9 +24,8 @@ resource "proxmox_virtual_environment_vm" "centos_vm" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_download_file" "centos_cloud_image" {
|
resource "proxmox_virtual_environment_download_file" "centos_cloud_image" {
|
||||||
content_type = "iso"
|
content_type = "import"
|
||||||
datastore_id = "local"
|
datastore_id = "local"
|
||||||
node_name = "pve"
|
node_name = "pve"
|
||||||
url = "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-latest.x86_64.qcow2"
|
url = "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-latest.x86_64.qcow2"
|
||||||
file_name = "centos8.img"
|
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,5 @@ terraform {
|
|||||||
provider "proxmox" {
|
provider "proxmox" {
|
||||||
endpoint = var.virtual_environment_endpoint
|
endpoint = var.virtual_environment_endpoint
|
||||||
api_token = var.virtual_environment_token
|
api_token = var.virtual_environment_token
|
||||||
ssh {
|
# SSH configuration is not required for this example
|
||||||
agent = true
|
|
||||||
username = "terraform"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ resource "proxmox_virtual_environment_vm" "debian_vm" {
|
|||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
# qcow2 image downloaded from https://cloud.debian.org/images/cloud/bookworm/latest/ and renamed to *.img
|
# qcow2 image downloaded from https://cloud.debian.org/images/cloud/bookworm/latest/ and renamed to *.img
|
||||||
|
# the image is not of import type, so provider will use SSH client to import it
|
||||||
file_id = "local:iso/debian-12-genericcloud-amd64.img"
|
file_id = "local:iso/debian-12-genericcloud-amd64.img"
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
|
@ -15,7 +15,7 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
|
|
||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
file_id = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
import_from = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
discard = "on"
|
discard = "on"
|
||||||
@ -24,8 +24,10 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
||||||
content_type = "iso"
|
content_type = "import"
|
||||||
datastore_id = "local"
|
datastore_id = "local"
|
||||||
node_name = "pve"
|
node_name = "pve"
|
||||||
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
||||||
|
# need to rename the file to *.qcow2 to indicate the actual file format for import
|
||||||
|
file_name = "jammy-server-cloudimg-amd64.qcow2"
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,4 @@ terraform {
|
|||||||
provider "proxmox" {
|
provider "proxmox" {
|
||||||
endpoint = var.virtual_environment_endpoint
|
endpoint = var.virtual_environment_endpoint
|
||||||
api_token = var.virtual_environment_token
|
api_token = var.virtual_environment_token
|
||||||
ssh {
|
|
||||||
agent = true
|
|
||||||
username = "terraform"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
|
|
||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
file_id = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
import_from = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
discard = "on"
|
discard = "on"
|
||||||
@ -40,11 +40,12 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
||||||
content_type = "iso"
|
content_type = "import"
|
||||||
datastore_id = "local"
|
datastore_id = "local"
|
||||||
node_name = "pve"
|
node_name = "pve"
|
||||||
|
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
||||||
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
# need to rename the file to *.qcow2 to indicate the actual file format for import
|
||||||
|
file_name = "jammy-server-cloudimg-amd64.qcow2"
|
||||||
}
|
}
|
||||||
|
|
||||||
output "vm_ipv4_address" {
|
output "vm_ipv4_address" {
|
||||||
|
@ -25,7 +25,7 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
|
|
||||||
disk {
|
disk {
|
||||||
datastore_id = "local-lvm"
|
datastore_id = "local-lvm"
|
||||||
file_id = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
import_from = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
|
||||||
interface = "virtio0"
|
interface = "virtio0"
|
||||||
iothread = true
|
iothread = true
|
||||||
discard = "on"
|
discard = "on"
|
||||||
@ -38,9 +38,10 @@ resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
|
||||||
content_type = "iso"
|
content_type = "import"
|
||||||
datastore_id = "local"
|
datastore_id = "local"
|
||||||
node_name = "pve"
|
node_name = "pve"
|
||||||
|
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
||||||
url = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
# need to rename the file to *.qcow2 to indicate the actual file format for import
|
||||||
|
file_name = "jammy-server-cloudimg-amd64.qcow2"
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,4 @@ terraform {
|
|||||||
provider "proxmox" {
|
provider "proxmox" {
|
||||||
endpoint = var.virtual_environment_endpoint
|
endpoint = var.virtual_environment_endpoint
|
||||||
api_token = var.virtual_environment_token
|
api_token = var.virtual_environment_token
|
||||||
ssh {
|
|
||||||
agent = true
|
|
||||||
username = "terraform"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -142,6 +142,45 @@ func TestAccResourceVMDisks(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}}},
|
}}},
|
||||||
|
{"import disk from an image", []resource.TestStep{{
|
||||||
|
Config: te.RenderConfig(`
|
||||||
|
resource "proxmox_virtual_environment_download_file" "test_disk_image" {
|
||||||
|
content_type = "import"
|
||||||
|
datastore_id = "local"
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
url = "{{.CloudImagesServer}}/jammy/current/jammy-server-cloudimg-amd64.img"
|
||||||
|
file_name = "test-disk-image.img.raw"
|
||||||
|
overwrite_unmanaged = true
|
||||||
|
}
|
||||||
|
resource "proxmox_virtual_environment_vm" "test_disk" {
|
||||||
|
node_name = "{{.NodeName}}"
|
||||||
|
started = false
|
||||||
|
name = "test-disk"
|
||||||
|
disk {
|
||||||
|
datastore_id = "local-lvm"
|
||||||
|
import_from = proxmox_virtual_environment_download_file.test_disk_image.id
|
||||||
|
interface = "virtio0"
|
||||||
|
iothread = true
|
||||||
|
discard = "on"
|
||||||
|
serial = "dead_beef"
|
||||||
|
size = 20
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
ResourceAttributes("proxmox_virtual_environment_vm.test_disk", map[string]string{
|
||||||
|
"disk.0.cache": "none",
|
||||||
|
"disk.0.datastore_id": "local-lvm",
|
||||||
|
"disk.0.discard": "on",
|
||||||
|
"disk.0.file_format": "raw",
|
||||||
|
"disk.0.interface": "virtio0",
|
||||||
|
"disk.0.iothread": "true",
|
||||||
|
"disk.0.path_in_datastore": `vm-\d+-disk-\d+`,
|
||||||
|
"disk.0.serial": "dead_beef",
|
||||||
|
"disk.0.size": "20",
|
||||||
|
"disk.0.ssd": "false",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}}},
|
||||||
{"clone default disk without overrides", []resource.TestStep{
|
{"clone default disk without overrides", []resource.TestStep{
|
||||||
{
|
{
|
||||||
Config: te.RenderConfig(`
|
Config: te.RenderConfig(`
|
||||||
|
@ -36,6 +36,7 @@ type CustomStorageDevice struct {
|
|||||||
BurstableWriteSpeedMbps *int `json:"mbps_wr_max,omitempty" url:"mbps_wr_max,omitempty"`
|
BurstableWriteSpeedMbps *int `json:"mbps_wr_max,omitempty" url:"mbps_wr_max,omitempty"`
|
||||||
Cache *string `json:"cache,omitempty" url:"cache,omitempty"`
|
Cache *string `json:"cache,omitempty" url:"cache,omitempty"`
|
||||||
Discard *string `json:"discard,omitempty" url:"discard,omitempty"`
|
Discard *string `json:"discard,omitempty" url:"discard,omitempty"`
|
||||||
|
ImportFrom *string `json:"import_from,omitempty" url:"import_from,omitempty"`
|
||||||
Format *string `json:"format,omitempty" url:"format,omitempty"`
|
Format *string `json:"format,omitempty" url:"format,omitempty"`
|
||||||
IopsRead *int `json:"iops_rd,omitempty" url:"iops_rd,omitempty"`
|
IopsRead *int `json:"iops_rd,omitempty" url:"iops_rd,omitempty"`
|
||||||
IopsWrite *int `json:"iops_wr,omitempty" url:"iops_wr,omitempty"`
|
IopsWrite *int `json:"iops_wr,omitempty" url:"iops_wr,omitempty"`
|
||||||
@ -203,10 +204,18 @@ func (d *CustomStorageDevice) EncodeOptions() string {
|
|||||||
|
|
||||||
// EncodeValues converts a CustomStorageDevice struct to a URL value.
|
// EncodeValues converts a CustomStorageDevice struct to a URL value.
|
||||||
func (d *CustomStorageDevice) EncodeValues(key string, v *url.Values) error {
|
func (d *CustomStorageDevice) EncodeValues(key string, v *url.Values) error {
|
||||||
|
if d.ImportFrom != nil && *d.ImportFrom != "" {
|
||||||
|
d.FileVolume = *d.DatastoreID + ":" + "0"
|
||||||
|
}
|
||||||
|
|
||||||
values := []string{
|
values := []string{
|
||||||
fmt.Sprintf("file=%s", d.FileVolume),
|
fmt.Sprintf("file=%s", d.FileVolume),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.ImportFrom != nil && *d.ImportFrom != "" {
|
||||||
|
values = append(values, fmt.Sprintf("import-from=%s", *d.ImportFrom))
|
||||||
|
}
|
||||||
|
|
||||||
if d.Format != nil {
|
if d.Format != nil {
|
||||||
values = append(values, fmt.Sprintf("format=%s", *d.Format))
|
values = append(values, fmt.Sprintf("format=%s", *d.Format))
|
||||||
}
|
}
|
||||||
@ -273,6 +282,9 @@ func (d *CustomStorageDevice) UnmarshalJSON(b []byte) error {
|
|||||||
case "file":
|
case "file":
|
||||||
d.FileVolume = v[1]
|
d.FileVolume = v[1]
|
||||||
|
|
||||||
|
case "import_from":
|
||||||
|
d.ImportFrom = &v[1]
|
||||||
|
|
||||||
case "format":
|
case "format":
|
||||||
d.Format = &v[1]
|
d.Format = &v[1]
|
||||||
|
|
||||||
@ -399,6 +411,7 @@ func (d *CustomStorageDevice) MergeWith(m CustomStorageDevice) bool {
|
|||||||
updated = ptr.UpdateIfChanged(&d.Replicate, m.Replicate) || updated
|
updated = ptr.UpdateIfChanged(&d.Replicate, m.Replicate) || updated
|
||||||
updated = ptr.UpdateIfChanged(&d.SSD, m.SSD) || updated
|
updated = ptr.UpdateIfChanged(&d.SSD, m.SSD) || updated
|
||||||
updated = ptr.UpdateIfChanged(&d.Serial, m.Serial) || updated
|
updated = ptr.UpdateIfChanged(&d.Serial, m.Serial) || updated
|
||||||
|
updated = ptr.UpdateIfChanged(&d.ImportFrom, m.ImportFrom) || updated
|
||||||
|
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
@ -178,6 +179,7 @@ func GetDiskDeviceObjects(
|
|||||||
diskInterface, _ := block[mkDiskInterface].(string)
|
diskInterface, _ := block[mkDiskInterface].(string)
|
||||||
fileFormat, _ := block[mkDiskFileFormat].(string)
|
fileFormat, _ := block[mkDiskFileFormat].(string)
|
||||||
fileID, _ := block[mkDiskFileID].(string)
|
fileID, _ := block[mkDiskFileID].(string)
|
||||||
|
importFrom, _ := block[mkDiskImportFrom].(string)
|
||||||
ioThread := types.CustomBool(block[mkDiskIOThread].(bool))
|
ioThread := types.CustomBool(block[mkDiskIOThread].(bool))
|
||||||
replicate := types.CustomBool(block[mkDiskReplicate].(bool))
|
replicate := types.CustomBool(block[mkDiskReplicate].(bool))
|
||||||
serial := block[mkDiskSerial].(string)
|
serial := block[mkDiskSerial].(string)
|
||||||
@ -212,6 +214,7 @@ func GetDiskDeviceObjects(
|
|||||||
diskDevice.DatastoreID = &datastoreID
|
diskDevice.DatastoreID = &datastoreID
|
||||||
diskDevice.Discard = &discard
|
diskDevice.Discard = &discard
|
||||||
diskDevice.FileID = &fileID
|
diskDevice.FileID = &fileID
|
||||||
|
diskDevice.ImportFrom = &importFrom
|
||||||
diskDevice.Replicate = &replicate
|
diskDevice.Replicate = &replicate
|
||||||
diskDevice.Serial = &serial
|
diskDevice.Serial = &serial
|
||||||
diskDevice.Size = types.DiskSizeFromGigabytes(int64(size))
|
diskDevice.Size = types.DiskSizeFromGigabytes(int64(size))
|
||||||
@ -418,6 +421,12 @@ func Read(
|
|||||||
disk[mkDiskFileID] = dd.FileID
|
disk[mkDiskFileID] = dd.FileID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note that PVE does not return back the 'import-from' attribute for the disks that are imported,
|
||||||
|
// but we'll keep it here for consistency. the actual value is set later down
|
||||||
|
if dd.ImportFrom != nil {
|
||||||
|
disk[mkDiskImportFrom] = dd.ImportFrom
|
||||||
|
}
|
||||||
|
|
||||||
disk[mkDiskInterface] = di
|
disk[mkDiskInterface] = di
|
||||||
disk[mkDiskSize] = dd.Size.InGigabytes()
|
disk[mkDiskSize] = dd.Size.InGigabytes()
|
||||||
|
|
||||||
@ -539,8 +548,20 @@ func Read(
|
|||||||
var diskList []interface{}
|
var diskList []interface{}
|
||||||
|
|
||||||
if len(currentDiskList) > 0 {
|
if len(currentDiskList) > 0 {
|
||||||
interfaces := utils.ListResourcesAttributeValue(currentDiskList, mkDiskInterface)
|
currentDiskMap := utils.MapResourcesByAttribute(currentDiskList, mkDiskInterface)
|
||||||
diskList = utils.OrderedListFromMapByKeyValues(diskMap, interfaces)
|
// copy import_from from the current disk if it exists
|
||||||
|
for k, v := range currentDiskMap {
|
||||||
|
if disk, ok := v.(map[string]interface{}); ok {
|
||||||
|
if importFrom, ok := disk[mkDiskImportFrom].(string); ok && importFrom != "" {
|
||||||
|
if _, exists := diskMap[k]; exists {
|
||||||
|
diskMap[k].(map[string]interface{})[mkDiskImportFrom] = importFrom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diskList = utils.OrderedListFromMapByKeyValues(diskMap,
|
||||||
|
slices.AppendSeq(make([]string, 0, len(currentDiskMap)), maps.Keys(currentDiskMap)))
|
||||||
} else {
|
} else {
|
||||||
diskList = utils.OrderedListFromMap(diskMap)
|
diskList = utils.OrderedListFromMap(diskMap)
|
||||||
}
|
}
|
||||||
@ -598,6 +619,13 @@ func Update(
|
|||||||
tmp.AIO = disk.AIO
|
tmp.AIO = disk.AIO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if disk.ImportFrom != nil && *disk.ImportFrom != "" {
|
||||||
|
rebootRequired = true
|
||||||
|
tmp.DatastoreID = disk.DatastoreID
|
||||||
|
tmp.ImportFrom = disk.ImportFrom
|
||||||
|
tmp.Size = disk.Size
|
||||||
|
}
|
||||||
|
|
||||||
tmp.Backup = disk.Backup
|
tmp.Backup = disk.Backup
|
||||||
tmp.BurstableReadSpeedMbps = disk.BurstableReadSpeedMbps
|
tmp.BurstableReadSpeedMbps = disk.BurstableReadSpeedMbps
|
||||||
tmp.BurstableWriteSpeedMbps = disk.BurstableWriteSpeedMbps
|
tmp.BurstableWriteSpeedMbps = disk.BurstableWriteSpeedMbps
|
||||||
|
@ -31,6 +31,7 @@ const (
|
|||||||
mkDiskDiscard = "discard"
|
mkDiskDiscard = "discard"
|
||||||
mkDiskFileFormat = "file_format"
|
mkDiskFileFormat = "file_format"
|
||||||
mkDiskFileID = "file_id"
|
mkDiskFileID = "file_id"
|
||||||
|
mkDiskImportFrom = "import_from"
|
||||||
mkDiskInterface = "interface"
|
mkDiskInterface = "interface"
|
||||||
mkDiskIopsRead = "iops_read"
|
mkDiskIopsRead = "iops_read"
|
||||||
mkDiskIopsReadBurstable = "iops_read_burstable"
|
mkDiskIopsReadBurstable = "iops_read_burstable"
|
||||||
@ -64,6 +65,7 @@ func Schema() map[string]*schema.Schema {
|
|||||||
mkDiskCache: dvDiskCache,
|
mkDiskCache: dvDiskCache,
|
||||||
mkDiskDatastoreID: dvDiskDatastoreID,
|
mkDiskDatastoreID: dvDiskDatastoreID,
|
||||||
mkDiskDiscard: dvDiskDiscard,
|
mkDiskDiscard: dvDiskDiscard,
|
||||||
|
mkDiskImportFrom: "",
|
||||||
mkDiskFileID: "",
|
mkDiskFileID: "",
|
||||||
mkDiskInterface: dvDiskInterface,
|
mkDiskInterface: dvDiskInterface,
|
||||||
mkDiskIOThread: false,
|
mkDiskIOThread: false,
|
||||||
@ -133,6 +135,14 @@ func Schema() map[string]*schema.Schema {
|
|||||||
Default: "",
|
Default: "",
|
||||||
ValidateDiagFunc: validators.FileID(),
|
ValidateDiagFunc: validators.FileID(),
|
||||||
},
|
},
|
||||||
|
mkDiskImportFrom: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The file id of a disk image to import from storage.",
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: false,
|
||||||
|
Default: "",
|
||||||
|
ValidateDiagFunc: validators.FileID(),
|
||||||
|
},
|
||||||
mkDiskSerial: {
|
mkDiskSerial: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Description: "The drive’s reported serial number",
|
Description: "The drive’s reported serial number",
|
||||||
|
@ -20,6 +20,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkDiskPathInDatastore,
|
mkDiskPathInDatastore,
|
||||||
mkDiskFileFormat,
|
mkDiskFileFormat,
|
||||||
mkDiskFileID,
|
mkDiskFileID,
|
||||||
|
mkDiskImportFrom,
|
||||||
mkDiskSize,
|
mkDiskSize,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkDiskPathInDatastore: schema.TypeString,
|
mkDiskPathInDatastore: schema.TypeString,
|
||||||
mkDiskFileFormat: schema.TypeString,
|
mkDiskFileFormat: schema.TypeString,
|
||||||
mkDiskFileID: schema.TypeString,
|
mkDiskFileID: schema.TypeString,
|
||||||
|
mkDiskImportFrom: schema.TypeString,
|
||||||
mkDiskSize: schema.TypeInt,
|
mkDiskSize: schema.TypeInt,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2913,6 +2913,25 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resizeDisks := diskDeviceObjects.Filter(func(device *vms.CustomStorageDevice) bool {
|
||||||
|
return device.ImportFrom != nil && *device.ImportFrom != ""
|
||||||
|
})
|
||||||
|
if len(resizeDisks) > 0 {
|
||||||
|
tflog.Info(ctx, "Resizing disks after VM creation")
|
||||||
|
|
||||||
|
for idev, device := range resizeDisks {
|
||||||
|
tflog.Info(ctx, fmt.Sprintf("VM %d: Resizing disk %s", vmID, idev))
|
||||||
|
|
||||||
|
err = client.Node(nodeName).VM(vmID).ResizeVMDisk(ctx, &vms.ResizeDiskRequestBody{
|
||||||
|
Size: *device.Size,
|
||||||
|
Disk: idev,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return diag.FromErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return vmCreateStart(ctx, d, m)
|
return vmCreateStart(ctx, d, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5908,6 +5927,11 @@ func vmUpdateDiskLocationAndSize(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to resize the disk if the import source has changed.
|
||||||
|
if *oldDisk.ImportFrom != *diskNewEntries[oldIface].ImportFrom {
|
||||||
|
*oldDisk.Size = 0
|
||||||
|
}
|
||||||
|
|
||||||
if *oldDisk.Size != *diskNewEntries[oldIface].Size {
|
if *oldDisk.Size != *diskNewEntries[oldIface].Size {
|
||||||
if *oldDisk.Size < *diskNewEntries[oldIface].Size {
|
if *oldDisk.Size < *diskNewEntries[oldIface].Size {
|
||||||
if oldDisk.IsOwnedBy(vmID) {
|
if oldDisk.IsOwnedBy(vmID) {
|
||||||
|
Loading…
Reference in New Issue
Block a user