mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
New module: Proxmox disk management (#5101)
* New module: Proxmox disk management * Remove misplaced option * Type missed * Fixed docs, quotes, 2.7 syntax * Forgotten comma * Version added 5.5.0 Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Italic options Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Missed dot Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Pythonify python Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Shorten command Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Action parameter drop. General improvements. * Add proxmox_disk integration testing * Shorten getting vmid Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Code tag for value Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Italic tag for option Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Definite ID of the VM Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Docs edit and loop condition * Simplify conditions Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Dropped bps options, added idempotency checks * Documentaion edit * Rewrite create/import condition * Trainling comma Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> * Added type field to all choosable arguments * Description of disk bus ranges * Fix imports * Update version Co-authored-by: Felix Fontein <felix@fontein.de> * Lowercase YAML boolean * Rename grown to resized and update documentation * Documentation updated before actual changes * Added 'update' flag for 'present' state * Traling space * YAML indentation * Merged 'updated' option into 'present'. * Doc update. * Exclude 'import_from' on update * Version bump * yaml boolean lowercase Co-authored-by: Felix Fontein <felix@fontein.de> * yaml boolean lowercase Co-authored-by: Felix Fontein <felix@fontein.de> * More detailed description Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
35e3a9615a
commit
7777b48c99
4 changed files with 944 additions and 0 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -378,6 +378,8 @@ files:
|
||||||
$modules/cloud/misc/proxmox_template.py:
|
$modules/cloud/misc/proxmox_template.py:
|
||||||
maintainers: UnderGreen
|
maintainers: UnderGreen
|
||||||
ignore: skvidal
|
ignore: skvidal
|
||||||
|
$modules/cloud/misc/proxmox_disk.py:
|
||||||
|
maintainers: castorsky
|
||||||
$modules/cloud/misc/rhevm.py:
|
$modules/cloud/misc/rhevm.py:
|
||||||
maintainers: $team_virt TimothyVandenbrande
|
maintainers: $team_virt TimothyVandenbrande
|
||||||
labels: rhevm virt
|
labels: rhevm virt
|
||||||
|
|
|
@ -1213,6 +1213,8 @@ plugin_routing:
|
||||||
redirect: community.general.cloud.profitbricks.profitbricks_volume_attachments
|
redirect: community.general.cloud.profitbricks.profitbricks_volume_attachments
|
||||||
proxmox:
|
proxmox:
|
||||||
redirect: community.general.cloud.misc.proxmox
|
redirect: community.general.cloud.misc.proxmox
|
||||||
|
proxmox_disk:
|
||||||
|
redirect: community.general.cloud.misc.proxmox_disk
|
||||||
proxmox_domain_info:
|
proxmox_domain_info:
|
||||||
redirect: community.general.cloud.misc.proxmox_domain_info
|
redirect: community.general.cloud.misc.proxmox_domain_info
|
||||||
proxmox_group_info:
|
proxmox_group_info:
|
||||||
|
|
744
plugins/modules/cloud/misc/proxmox_disk.py
Normal file
744
plugins/modules/cloud/misc/proxmox_disk.py
Normal file
|
@ -0,0 +1,744 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2022, Castor Sky (@castorsky) <csky57@gmail.com>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: proxmox_disk
|
||||||
|
short_description: Management of a disk of a Qemu(KVM) VM in a Proxmox VE cluster.
|
||||||
|
version_added: 5.7.0
|
||||||
|
description:
|
||||||
|
- Allows you to perform some supported operations on a disk in Qemu(KVM) Virtual Machines in a Proxmox VE cluster.
|
||||||
|
author: "Castor Sky (@castorsky) <csky57@gmail.com>"
|
||||||
|
options:
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- The unique name of the VM.
|
||||||
|
- You can specify either I(name) or I(vmid) or both of them.
|
||||||
|
type: str
|
||||||
|
vmid:
|
||||||
|
description:
|
||||||
|
- The unique ID of the VM.
|
||||||
|
- You can specify either I(vmid) or I(name) or both of them.
|
||||||
|
type: int
|
||||||
|
disk:
|
||||||
|
description:
|
||||||
|
- The disk key (C(unused[n]), C(ide[n]), C(sata[n]), C(scsi[n]) or C(virtio[n])) you want to operate on.
|
||||||
|
- Disk buses (IDE, SATA and so on) have fixed ranges of C(n) that accepted by Proxmox API.
|
||||||
|
- >
|
||||||
|
For IDE: 0-3;
|
||||||
|
for SCSI: 0-30;
|
||||||
|
for SATA: 0-5;
|
||||||
|
for VirtIO: 0-15;
|
||||||
|
for Unused: 0-255.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- Indicates desired state of the disk.
|
||||||
|
- >
|
||||||
|
I(state=present) can be used to create, replace disk or update options in existing disk. It will create missing
|
||||||
|
disk or update options in existing one by default. See the I(create) parameter description to control behavior
|
||||||
|
of this option.
|
||||||
|
- Some updates on options (like I(cache)) are not being applied instantly and require VM restart.
|
||||||
|
- >
|
||||||
|
Use I(state=detached) to detach existing disk from VM but do not remove it entirely.
|
||||||
|
When I(state=detached) and disk is C(unused[n]) it will be left in same state (not removed).
|
||||||
|
- >
|
||||||
|
I(state=moved) may be used to change backing storage for the disk in bounds of the same VM
|
||||||
|
or to send the disk to another VM (using the same backing storage).
|
||||||
|
- >
|
||||||
|
I(state=resized) intended to change the disk size. As of Proxmox 7.2 you can only increase the disk size
|
||||||
|
because shrinking disks is not supported by the PVE API and has to be done manually.
|
||||||
|
- To entirely remove the disk from backing storage use I(state=absent).
|
||||||
|
type: str
|
||||||
|
choices: ['present', 'resized', 'detached', 'moved', 'absent']
|
||||||
|
default: present
|
||||||
|
create:
|
||||||
|
description:
|
||||||
|
- With I(create) flag you can control behavior of I(state=present).
|
||||||
|
- When I(create=disabled) it will not create new disk (if not exists) but will update options in existing disk.
|
||||||
|
- When I(create=regular) it will either create new disk (if not exists) or update options in existing disk.
|
||||||
|
- When I(create=forced) it will always create new disk (if disk exists it will be detached and left unused).
|
||||||
|
type: str
|
||||||
|
choices: ['disabled', 'regular', 'forced']
|
||||||
|
default: regular
|
||||||
|
storage:
|
||||||
|
description:
|
||||||
|
- The drive's backing storage.
|
||||||
|
- Used only when I(state) is C(present).
|
||||||
|
type: str
|
||||||
|
size:
|
||||||
|
description:
|
||||||
|
- Desired volume size in GB to allocate when I(state=present) (specify I(size) without suffix).
|
||||||
|
- >
|
||||||
|
New (or additional) size of volume when I(state=resized). With the C(+) sign
|
||||||
|
the value is added to the actual size of the volume
|
||||||
|
and without it, the value is taken as an absolute one.
|
||||||
|
type: str
|
||||||
|
bwlimit:
|
||||||
|
description:
|
||||||
|
- Override I/O bandwidth limit (in KB/s).
|
||||||
|
- Used only when I(state=moved).
|
||||||
|
type: int
|
||||||
|
delete_moved:
|
||||||
|
description:
|
||||||
|
- Delete the original disk after successful copy.
|
||||||
|
- By default the original disk is kept as unused disk.
|
||||||
|
- Used only when I(state=moved).
|
||||||
|
type: bool
|
||||||
|
target_disk:
|
||||||
|
description:
|
||||||
|
- The config key the disk will be moved to on the target VM (for example, C(ide0) or C(scsi1)).
|
||||||
|
- Default is the source disk key.
|
||||||
|
- Used only when I(state=moved).
|
||||||
|
type: str
|
||||||
|
target_storage:
|
||||||
|
description:
|
||||||
|
- Move the disk to this storage when I(state=moved).
|
||||||
|
- You can move between storages only in scope of one VM.
|
||||||
|
- Mutually exclusive with I(target_vmid).
|
||||||
|
type: str
|
||||||
|
target_vmid:
|
||||||
|
description:
|
||||||
|
- The (unique) ID of the VM where disk will be placed when I(state=moved).
|
||||||
|
- You can move disk between VMs only when the same storage is used.
|
||||||
|
- Mutually exclusive with I(target_vmid).
|
||||||
|
type: int
|
||||||
|
timeout:
|
||||||
|
description:
|
||||||
|
- Timeout in seconds to wait when moving disk.
|
||||||
|
- Used only when I(state=moved).
|
||||||
|
type: int
|
||||||
|
default: 600
|
||||||
|
aio:
|
||||||
|
description:
|
||||||
|
- AIO type to use.
|
||||||
|
type: str
|
||||||
|
choices: ['native', 'threads', 'io_uring']
|
||||||
|
backup:
|
||||||
|
description:
|
||||||
|
- Whether the drive should be included when making backups.
|
||||||
|
type: bool
|
||||||
|
bps_max_length:
|
||||||
|
description:
|
||||||
|
- Maximum length of total r/w I/O bursts in seconds.
|
||||||
|
type: int
|
||||||
|
bps_rd_max_length:
|
||||||
|
description:
|
||||||
|
- Maximum length of read I/O bursts in seconds.
|
||||||
|
type: int
|
||||||
|
bps_wr_max_length:
|
||||||
|
description:
|
||||||
|
- Maximum length of write I/O bursts in seconds.
|
||||||
|
type: int
|
||||||
|
cache:
|
||||||
|
description:
|
||||||
|
- The drive's cache mode.
|
||||||
|
type: str
|
||||||
|
choices: ['none', 'writethrough', 'writeback', 'unsafe', 'directsync']
|
||||||
|
cyls:
|
||||||
|
description:
|
||||||
|
- Force the drive's physical geometry to have a specific cylinder count.
|
||||||
|
type: int
|
||||||
|
detect_zeroes:
|
||||||
|
description:
|
||||||
|
- Control whether to detect and try to optimize writes of zeroes.
|
||||||
|
type: bool
|
||||||
|
discard:
|
||||||
|
description:
|
||||||
|
- Control whether to pass discard/trim requests to the underlying storage.
|
||||||
|
type: str
|
||||||
|
choices: ['ignore', 'on']
|
||||||
|
format:
|
||||||
|
description:
|
||||||
|
- The drive's backing file's data format.
|
||||||
|
type: str
|
||||||
|
choices: ['raw', 'cow', 'qcow', 'qed', 'qcow2', 'vmdk', 'cloop']
|
||||||
|
heads:
|
||||||
|
description:
|
||||||
|
- Force the drive's physical geometry to have a specific head count.
|
||||||
|
type: int
|
||||||
|
import_from:
|
||||||
|
description:
|
||||||
|
- Import volume from this existing one.
|
||||||
|
- Volume string format
|
||||||
|
- C(<STORAGE>:<VMID>/<FULL_NAME>) or C(<ABSOLUTE_PATH>/<FULL_NAME>)
|
||||||
|
- Attention! Only root can use absolute paths.
|
||||||
|
- This parameter is mutually exclusive with I(size).
|
||||||
|
type: str
|
||||||
|
iops:
|
||||||
|
description:
|
||||||
|
- Maximum total r/w I/O in operations per second.
|
||||||
|
- You can specify either total limit or per operation (mutually exclusive with I(iops_rd) and I(iops_wr)).
|
||||||
|
type: int
|
||||||
|
iops_max:
|
||||||
|
description:
|
||||||
|
- Maximum unthrottled total r/w I/O pool in operations per second.
|
||||||
|
type: int
|
||||||
|
iops_max_length:
|
||||||
|
description:
|
||||||
|
- Maximum length of total r/w I/O bursts in seconds.
|
||||||
|
type: int
|
||||||
|
iops_rd:
|
||||||
|
description:
|
||||||
|
- Maximum read I/O in operations per second.
|
||||||
|
- You can specify either read or total limit (mutually exclusive with I(iops)).
|
||||||
|
type: int
|
||||||
|
iops_rd_max:
|
||||||
|
description:
|
||||||
|
- Maximum unthrottled read I/O pool in operations per second.
|
||||||
|
type: int
|
||||||
|
iops_rd_max_length:
|
||||||
|
description:
|
||||||
|
- Maximum length of read I/O bursts in seconds.
|
||||||
|
type: int
|
||||||
|
iops_wr:
|
||||||
|
description:
|
||||||
|
- Maximum write I/O in operations per second.
|
||||||
|
- You can specify either write or total limit (mutually exclusive with I(iops)).
|
||||||
|
type: int
|
||||||
|
iops_wr_max:
|
||||||
|
description:
|
||||||
|
- Maximum unthrottled write I/O pool in operations per second.
|
||||||
|
type: int
|
||||||
|
iops_wr_max_length:
|
||||||
|
description:
|
||||||
|
- Maximum length of write I/O bursts in seconds.
|
||||||
|
type: int
|
||||||
|
iothread:
|
||||||
|
description:
|
||||||
|
- Whether to use iothreads for this drive (only for SCSI and VirtIO)
|
||||||
|
type: bool
|
||||||
|
mbps:
|
||||||
|
description:
|
||||||
|
- Maximum total r/w speed in megabytes per second.
|
||||||
|
- Can be fractional but use with caution - fractionals less than 1 are not supported officially.
|
||||||
|
- You can specify either total limit or per operation (mutually exclusive with I(mbps_rd) and I(mbps_wr)).
|
||||||
|
type: float
|
||||||
|
mbps_max:
|
||||||
|
description:
|
||||||
|
- Maximum unthrottled total r/w pool in megabytes per second.
|
||||||
|
type: float
|
||||||
|
mbps_rd:
|
||||||
|
description:
|
||||||
|
- Maximum read speed in megabytes per second.
|
||||||
|
- You can specify either read or total limit (mutually exclusive with I(mbps)).
|
||||||
|
type: float
|
||||||
|
mbps_rd_max:
|
||||||
|
description:
|
||||||
|
- Maximum unthrottled read pool in megabytes per second.
|
||||||
|
type: float
|
||||||
|
mbps_wr:
|
||||||
|
description:
|
||||||
|
- Maximum write speed in megabytes per second.
|
||||||
|
- You can specify either write or total limit (mutually exclusive with I(mbps)).
|
||||||
|
type: float
|
||||||
|
mbps_wr_max:
|
||||||
|
description:
|
||||||
|
- Maximum unthrottled write pool in megabytes per second.
|
||||||
|
type: float
|
||||||
|
media:
|
||||||
|
description:
|
||||||
|
- The drive's media type.
|
||||||
|
type: str
|
||||||
|
choices: ['cdrom', 'disk']
|
||||||
|
queues:
|
||||||
|
description:
|
||||||
|
- Number of queues (SCSI only).
|
||||||
|
type: int
|
||||||
|
replicate:
|
||||||
|
description:
|
||||||
|
- Whether the drive should considered for replication jobs.
|
||||||
|
type: bool
|
||||||
|
rerror:
|
||||||
|
description:
|
||||||
|
- Read error action.
|
||||||
|
type: str
|
||||||
|
choices: ['ignore', 'report', 'stop']
|
||||||
|
ro:
|
||||||
|
description:
|
||||||
|
- Whether the drive is read-only.
|
||||||
|
type: bool
|
||||||
|
scsiblock:
|
||||||
|
description:
|
||||||
|
- Whether to use scsi-block for full passthrough of host block device.
|
||||||
|
- Can lead to I/O errors in combination with low memory or high memory fragmentation on host.
|
||||||
|
type: bool
|
||||||
|
secs:
|
||||||
|
description:
|
||||||
|
- Force the drive's physical geometry to have a specific sector count.
|
||||||
|
type: int
|
||||||
|
serial:
|
||||||
|
description:
|
||||||
|
- The drive's reported serial number, url-encoded, up to 20 bytes long.
|
||||||
|
type: str
|
||||||
|
shared:
|
||||||
|
description:
|
||||||
|
- Mark this locally-managed volume as available on all nodes.
|
||||||
|
- This option does not share the volume automatically, it assumes it is shared already!
|
||||||
|
type: bool
|
||||||
|
snapshot:
|
||||||
|
description:
|
||||||
|
- Control qemu's snapshot mode feature.
|
||||||
|
- If activated, changes made to the disk are temporary and will be discarded when the VM is shutdown.
|
||||||
|
type: bool
|
||||||
|
ssd:
|
||||||
|
description:
|
||||||
|
- Whether to expose this drive as an SSD, rather than a rotational hard disk.
|
||||||
|
type: bool
|
||||||
|
trans:
|
||||||
|
description:
|
||||||
|
- Force disk geometry bios translation mode.
|
||||||
|
type: str
|
||||||
|
choices: ['auto', 'lba', 'none']
|
||||||
|
werror:
|
||||||
|
description:
|
||||||
|
- Write error action.
|
||||||
|
type: str
|
||||||
|
choices: ['enospc', 'ignore', 'report', 'stop']
|
||||||
|
wwn:
|
||||||
|
description:
|
||||||
|
- The drive's worldwide name, encoded as 16 bytes hex string, prefixed by C(0x).
|
||||||
|
type: str
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- community.general.proxmox.documentation
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = '''
|
||||||
|
- name: Create new disk in VM (do not rewrite in case it exists already)
|
||||||
|
community.general.proxmox_disk:
|
||||||
|
api_host: node1
|
||||||
|
api_user: root@pam
|
||||||
|
api_token_id: token1
|
||||||
|
api_token_secret: some-token-data
|
||||||
|
name: vm-name
|
||||||
|
disk: scsi3
|
||||||
|
backup: true
|
||||||
|
cache: none
|
||||||
|
storage: local-zfs
|
||||||
|
size: 5
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Create new disk in VM (force rewrite in case it exists already)
|
||||||
|
community.general.proxmox_disk:
|
||||||
|
api_host: node1
|
||||||
|
api_user: root@pam
|
||||||
|
api_token_id: token1
|
||||||
|
api_token_secret: some-token-data
|
||||||
|
vmid: 101
|
||||||
|
disk: scsi3
|
||||||
|
format: qcow2
|
||||||
|
storage: local
|
||||||
|
size: 16
|
||||||
|
create: forced
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update existing disk
|
||||||
|
community.general.proxmox_disk:
|
||||||
|
api_host: node1
|
||||||
|
api_user: root@pam
|
||||||
|
api_token_id: token1
|
||||||
|
api_token_secret: some-token-data
|
||||||
|
vmid: 101
|
||||||
|
disk: ide0
|
||||||
|
backup: false
|
||||||
|
ro: true
|
||||||
|
aio: native
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Grow existing disk
|
||||||
|
community.general.proxmox_disk:
|
||||||
|
api_host: node1
|
||||||
|
api_user: root@pam
|
||||||
|
api_token_id: token1
|
||||||
|
api_token_secret: some-token-data
|
||||||
|
vmid: 101
|
||||||
|
disk: sata4
|
||||||
|
size: +5G
|
||||||
|
state: resized
|
||||||
|
|
||||||
|
- name: Detach disk (leave it unused)
|
||||||
|
community.general.proxmox_disk:
|
||||||
|
api_host: node1
|
||||||
|
api_user: root@pam
|
||||||
|
api_token_id: token1
|
||||||
|
api_token_secret: some-token-data
|
||||||
|
name: vm-name
|
||||||
|
disk: virtio0
|
||||||
|
state: detached
|
||||||
|
|
||||||
|
- name: Move disk to another storage
|
||||||
|
community.general.proxmox_disk:
|
||||||
|
api_host: node1
|
||||||
|
api_user: root@pam
|
||||||
|
api_password: secret
|
||||||
|
vmid: 101
|
||||||
|
disk: scsi7
|
||||||
|
target_storage: local
|
||||||
|
format: qcow2
|
||||||
|
state: moved
|
||||||
|
|
||||||
|
- name: Move disk from one VM to another
|
||||||
|
community.general.proxmox_disk:
|
||||||
|
api_host: node1
|
||||||
|
api_user: root@pam
|
||||||
|
api_token_id: token1
|
||||||
|
api_token_secret: some-token-data
|
||||||
|
vmid: 101
|
||||||
|
disk: scsi7
|
||||||
|
target_vmid: 201
|
||||||
|
state: moved
|
||||||
|
|
||||||
|
- name: Remove disk permanently
|
||||||
|
community.general.proxmox_disk:
|
||||||
|
api_host: node1
|
||||||
|
api_user: root@pam
|
||||||
|
api_password: secret
|
||||||
|
vmid: 101
|
||||||
|
disk: scsi4
|
||||||
|
state: absent
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = '''
|
||||||
|
vmid:
|
||||||
|
description: The VM vmid.
|
||||||
|
returned: success
|
||||||
|
type: int
|
||||||
|
sample: 101
|
||||||
|
msg:
|
||||||
|
description: A short message on what the module did.
|
||||||
|
returned: always
|
||||||
|
type: str
|
||||||
|
sample: "Disk scsi3 created in VM 101"
|
||||||
|
'''
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible_collections.community.general.plugins.module_utils.proxmox import (proxmox_auth_argument_spec,
|
||||||
|
ProxmoxAnsible)
|
||||||
|
from re import compile, match, sub
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
|
def disk_conf_str_to_dict(config_string):
|
||||||
|
config = config_string.split(',')
|
||||||
|
storage_volume = config.pop(0).split(':')
|
||||||
|
config.sort()
|
||||||
|
storage_name = storage_volume[0]
|
||||||
|
volume_name = storage_volume[1]
|
||||||
|
config_current = dict(
|
||||||
|
volume='%s:%s' % (storage_name, volume_name),
|
||||||
|
storage_name=storage_name,
|
||||||
|
volume_name=volume_name
|
||||||
|
)
|
||||||
|
|
||||||
|
for option in config:
|
||||||
|
k, v = option.split('=')
|
||||||
|
config_current[k] = v
|
||||||
|
|
||||||
|
return config_current
|
||||||
|
|
||||||
|
|
||||||
|
class ProxmoxDiskAnsible(ProxmoxAnsible):
|
||||||
|
create_update_fields = [
|
||||||
|
'aio', 'backup', 'bps_max_length', 'bps_rd_max_length', 'bps_wr_max_length',
|
||||||
|
'cache', 'cyls', 'detect_zeroes', 'discard', 'format', 'heads', 'import_from', 'iops', 'iops_max',
|
||||||
|
'iops_max_length', 'iops_rd', 'iops_rd_max', 'iops_rd_max_length', 'iops_wr', 'iops_wr_max',
|
||||||
|
'iops_wr_max_length', 'iothread', 'mbps', 'mbps_max', 'mbps_rd', 'mbps_rd_max', 'mbps_wr', 'mbps_wr_max',
|
||||||
|
'media', 'queues', 'replicate', 'rerror', 'ro', 'scsiblock', 'secs', 'serial', 'shared', 'snapshot',
|
||||||
|
'ssd', 'trans', 'werror', 'wwn'
|
||||||
|
]
|
||||||
|
supported_bus_num_ranges = dict(
|
||||||
|
ide=range(0, 4),
|
||||||
|
scsi=range(0, 31),
|
||||||
|
sata=range(0, 6),
|
||||||
|
virtio=range(0, 16),
|
||||||
|
unused=range(0, 256)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_create_attributes(self):
|
||||||
|
# Sanitize parameters dictionary:
|
||||||
|
# - Remove not defined args
|
||||||
|
# - Ensure True and False converted to int.
|
||||||
|
# - Remove unnecessary parameters
|
||||||
|
params = dict((k, v) for k, v in self.module.params.items() if v is not None and k in self.create_update_fields)
|
||||||
|
params.update(dict((k, int(v)) for k, v in params.items() if isinstance(v, bool)))
|
||||||
|
return params
|
||||||
|
|
||||||
|
def create_disk(self, disk, vmid, vm, vm_config):
|
||||||
|
create = self.module.params['create']
|
||||||
|
if create == 'disabled' and disk not in vm_config:
|
||||||
|
# NOOP
|
||||||
|
return False, "Disk %s not found in VM %s and creation was disabled in parameters." % (disk, vmid)
|
||||||
|
|
||||||
|
if (create == 'regular' and disk not in vm_config) or (create == 'forced'):
|
||||||
|
# CREATE
|
||||||
|
attributes = self.get_create_attributes()
|
||||||
|
import_string = attributes.pop('import_from', None)
|
||||||
|
|
||||||
|
if import_string:
|
||||||
|
config_str = "%s:%s,import-from=%s" % (self.module.params["storage"], "0", import_string)
|
||||||
|
else:
|
||||||
|
config_str = "%s:%s" % (self.module.params["storage"], self.module.params["size"])
|
||||||
|
|
||||||
|
for k, v in attributes.items():
|
||||||
|
config_str += ',%s=%s' % (k, v)
|
||||||
|
|
||||||
|
create_disk = {self.module.params["disk"]: config_str}
|
||||||
|
self.proxmox_api.nodes(vm['node']).qemu(vmid).config.set(**create_disk)
|
||||||
|
return True, "Disk %s created in VM %s" % (disk, vmid)
|
||||||
|
|
||||||
|
if create in ['disabled', 'regular'] and disk in vm_config:
|
||||||
|
# UPDATE
|
||||||
|
disk_config = disk_conf_str_to_dict(vm_config[disk])
|
||||||
|
config_str = disk_config["volume"]
|
||||||
|
attributes = self.get_create_attributes()
|
||||||
|
# 'import_from' fails on disk updates
|
||||||
|
attributes.pop('import_from', None)
|
||||||
|
|
||||||
|
for k, v in attributes.items():
|
||||||
|
config_str += ',%s=%s' % (k, v)
|
||||||
|
|
||||||
|
# Now compare old and new config to detect if changes are needed
|
||||||
|
for option in ['size', 'storage_name', 'volume', 'volume_name']:
|
||||||
|
attributes.update({option: disk_config[option]})
|
||||||
|
# Values in params are numbers, but strings are needed to compare with disk_config
|
||||||
|
attributes = dict((k, str(v)) for k, v in attributes.items())
|
||||||
|
if disk_config == attributes:
|
||||||
|
return False, "Disk %s is up to date in VM %s" % (disk, vmid)
|
||||||
|
|
||||||
|
update_disk = {self.module.params["disk"]: config_str}
|
||||||
|
self.proxmox_api.nodes(vm['node']).qemu(vmid).config.set(**update_disk)
|
||||||
|
return True, "Disk %s updated in VM %s" % (disk, vmid)
|
||||||
|
|
||||||
|
def move_disk(self, disk, vmid, vm, vm_config):
|
||||||
|
params = dict()
|
||||||
|
params['disk'] = disk
|
||||||
|
params['vmid'] = vmid
|
||||||
|
params['bwlimit'] = self.module.params['bwlimit']
|
||||||
|
params['storage'] = self.module.params['target_storage']
|
||||||
|
params['target-disk'] = self.module.params['target_disk']
|
||||||
|
params['target-vmid'] = self.module.params['target_vmid']
|
||||||
|
params['format'] = self.module.params['format']
|
||||||
|
params['delete'] = 1 if self.module.params.get('delete_moved', False) else 0
|
||||||
|
# Remove not defined args
|
||||||
|
params = dict((k, v) for k, v in params.items() if v is not None)
|
||||||
|
|
||||||
|
if params.get('storage', False):
|
||||||
|
disk_config = disk_conf_str_to_dict(vm_config[disk])
|
||||||
|
if params['storage'] == disk_config['storage_name']:
|
||||||
|
return False
|
||||||
|
|
||||||
|
taskid = self.proxmox_api.nodes(vm['node']).qemu(vmid).move_disk.post(**params)
|
||||||
|
timeout = self.module.params['timeout']
|
||||||
|
while timeout:
|
||||||
|
status_data = self.proxmox_api.nodes(vm['node']).tasks(taskid).status.get()
|
||||||
|
if status_data['status'] == 'stopped' and status_data['exitstatus'] == 'OK':
|
||||||
|
return True
|
||||||
|
if timeout <= 0:
|
||||||
|
self.module.fail_json(
|
||||||
|
msg='Reached timeout while waiting for moving VM disk. Last line in task before timeout: %s' %
|
||||||
|
self.proxmox_api.nodes(vm['node']).tasks(taskid).log.get()[:1])
|
||||||
|
|
||||||
|
sleep(1)
|
||||||
|
timeout -= 1
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module_args = proxmox_auth_argument_spec()
|
||||||
|
disk_args = dict(
|
||||||
|
# Proxmox native parameters
|
||||||
|
aio=dict(type='str', choices=['native', 'threads', 'io_uring']),
|
||||||
|
backup=dict(type='bool'),
|
||||||
|
bps_max_length=dict(type='int'),
|
||||||
|
bps_rd_max_length=dict(type='int'),
|
||||||
|
bps_wr_max_length=dict(type='int'),
|
||||||
|
cache=dict(type='str', choices=['none', 'writethrough', 'writeback', 'unsafe', 'directsync']),
|
||||||
|
cyls=dict(type='int'),
|
||||||
|
detect_zeroes=dict(type='bool'),
|
||||||
|
discard=dict(type='str', choices=['ignore', 'on']),
|
||||||
|
format=dict(type='str', choices=['raw', 'cow', 'qcow', 'qed', 'qcow2', 'vmdk', 'cloop']),
|
||||||
|
heads=dict(type='int'),
|
||||||
|
import_from=dict(type='str'),
|
||||||
|
iops=dict(type='int'),
|
||||||
|
iops_max=dict(type='int'),
|
||||||
|
iops_max_length=dict(type='int'),
|
||||||
|
iops_rd=dict(type='int'),
|
||||||
|
iops_rd_max=dict(type='int'),
|
||||||
|
iops_rd_max_length=dict(type='int'),
|
||||||
|
iops_wr=dict(type='int'),
|
||||||
|
iops_wr_max=dict(type='int'),
|
||||||
|
iops_wr_max_length=dict(type='int'),
|
||||||
|
iothread=dict(type='bool'),
|
||||||
|
mbps=dict(type='float'),
|
||||||
|
mbps_max=dict(type='float'),
|
||||||
|
mbps_rd=dict(type='float'),
|
||||||
|
mbps_rd_max=dict(type='float'),
|
||||||
|
mbps_wr=dict(type='float'),
|
||||||
|
mbps_wr_max=dict(type='float'),
|
||||||
|
media=dict(type='str', choices=['cdrom', 'disk']),
|
||||||
|
queues=dict(type='int'),
|
||||||
|
replicate=dict(type='bool'),
|
||||||
|
rerror=dict(type='str', choices=['ignore', 'report', 'stop']),
|
||||||
|
ro=dict(type='bool'),
|
||||||
|
scsiblock=dict(type='bool'),
|
||||||
|
secs=dict(type='int'),
|
||||||
|
serial=dict(type='str'),
|
||||||
|
shared=dict(type='bool'),
|
||||||
|
snapshot=dict(type='bool'),
|
||||||
|
ssd=dict(type='bool'),
|
||||||
|
trans=dict(type='str', choices=['auto', 'lba', 'none']),
|
||||||
|
werror=dict(type='str', choices=['enospc', 'ignore', 'report', 'stop']),
|
||||||
|
wwn=dict(type='str'),
|
||||||
|
|
||||||
|
# Disk moving relates parameters
|
||||||
|
bwlimit=dict(type='int'),
|
||||||
|
target_storage=dict(type='str'),
|
||||||
|
target_disk=dict(type='str'),
|
||||||
|
target_vmid=dict(type='int'),
|
||||||
|
delete_moved=dict(type='bool'),
|
||||||
|
timeout=dict(type='int', default='600'),
|
||||||
|
|
||||||
|
# Module related parameters
|
||||||
|
name=dict(type='str'),
|
||||||
|
vmid=dict(type='int'),
|
||||||
|
disk=dict(type='str', required=True),
|
||||||
|
storage=dict(type='str'),
|
||||||
|
size=dict(type='str'),
|
||||||
|
state=dict(type='str', choices=['present', 'resized', 'detached', 'moved', 'absent'],
|
||||||
|
default='present'),
|
||||||
|
create=dict(type='str', choices=['disabled', 'regular', 'forced'], default='regular'),
|
||||||
|
)
|
||||||
|
|
||||||
|
module_args.update(disk_args)
|
||||||
|
|
||||||
|
module = AnsibleModule(
|
||||||
|
argument_spec=module_args,
|
||||||
|
required_together=[('api_token_id', 'api_token_secret')],
|
||||||
|
required_one_of=[('name', 'vmid'), ('api_password', 'api_token_id')],
|
||||||
|
required_if=[
|
||||||
|
('create', 'forced', ['storage']),
|
||||||
|
('state', 'resized', ['size']),
|
||||||
|
],
|
||||||
|
required_by={
|
||||||
|
'target_disk': 'target_vmid',
|
||||||
|
'mbps_max': 'mbps',
|
||||||
|
'mbps_rd_max': 'mbps_rd',
|
||||||
|
'mbps_wr_max': 'mbps_wr',
|
||||||
|
'bps_max_length': 'mbps_max',
|
||||||
|
'bps_rd_max_length': 'mbps_rd_max',
|
||||||
|
'bps_wr_max_length': 'mbps_wr_max',
|
||||||
|
'iops_max': 'iops',
|
||||||
|
'iops_rd_max': 'iops_rd',
|
||||||
|
'iops_wr_max': 'iops_wr',
|
||||||
|
'iops_max_length': 'iops_max',
|
||||||
|
'iops_rd_max_length': 'iops_rd_max',
|
||||||
|
'iops_wr_max_length': 'iops_wr_max',
|
||||||
|
},
|
||||||
|
supports_check_mode=False,
|
||||||
|
mutually_exclusive=[
|
||||||
|
('target_vmid', 'target_storage'),
|
||||||
|
('mbps', 'mbps_rd'),
|
||||||
|
('mbps', 'mbps_wr'),
|
||||||
|
('iops', 'iops_rd'),
|
||||||
|
('iops', 'iops_wr'),
|
||||||
|
('import_from', 'size'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
proxmox = ProxmoxDiskAnsible(module)
|
||||||
|
|
||||||
|
disk = module.params['disk']
|
||||||
|
# Verify disk name has appropriate name
|
||||||
|
disk_regex = compile(r'^([a-z]+)([0-9]+)$')
|
||||||
|
disk_bus = sub(disk_regex, r'\1', disk)
|
||||||
|
disk_number = int(sub(disk_regex, r'\2', disk))
|
||||||
|
if disk_bus not in proxmox.supported_bus_num_ranges:
|
||||||
|
proxmox.module.fail_json(msg='Unsupported disk bus: %s' % disk_bus)
|
||||||
|
elif disk_number not in proxmox.supported_bus_num_ranges[disk_bus]:
|
||||||
|
bus_range = proxmox.supported_bus_num_ranges[disk_bus]
|
||||||
|
proxmox.module.fail_json(msg='Disk %s number not in range %s..%s ' % (disk, bus_range[0], bus_range[-1]))
|
||||||
|
|
||||||
|
name = module.params['name']
|
||||||
|
state = module.params['state']
|
||||||
|
vmid = module.params['vmid'] or proxmox.get_vmid(name)
|
||||||
|
|
||||||
|
# Ensure VM id exists and retrieve its config
|
||||||
|
vm = None
|
||||||
|
vm_config = None
|
||||||
|
try:
|
||||||
|
vm = proxmox.get_vm(vmid)
|
||||||
|
vm_config = proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).config.get()
|
||||||
|
except Exception as e:
|
||||||
|
proxmox.module.fail_json(msg='Getting information for VM %s failed with exception: %s' % (vmid, str(e)))
|
||||||
|
|
||||||
|
# Do not try to perform actions on missing disk
|
||||||
|
if disk not in vm_config and state in ['resized', 'moved']:
|
||||||
|
module.fail_json(vmid=vmid, msg='Unable to process missing disk %s in VM %s' % (disk, vmid))
|
||||||
|
|
||||||
|
if state == 'present':
|
||||||
|
try:
|
||||||
|
success, message = proxmox.create_disk(disk, vmid, vm, vm_config)
|
||||||
|
if success:
|
||||||
|
module.exit_json(changed=True, vmid=vmid, msg=message)
|
||||||
|
else:
|
||||||
|
module.exit_json(changed=False, vmid=vmid, msg=message)
|
||||||
|
except Exception as e:
|
||||||
|
module.fail_json(vmid=vmid, msg='Unable to create/update disk %s in VM %s: %s' % (disk, vmid, str(e)))
|
||||||
|
|
||||||
|
elif state == 'detached':
|
||||||
|
try:
|
||||||
|
if disk_bus == 'unused':
|
||||||
|
module.exit_json(changed=False, vmid=vmid, msg='Disk %s already detached in VM %s' % (disk, vmid))
|
||||||
|
if disk not in vm_config:
|
||||||
|
module.exit_json(changed=False, vmid=vmid, msg="Disk %s not present in VM %s config" % (disk, vmid))
|
||||||
|
proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).unlink.put(vmid=vmid, idlist=disk, force=0)
|
||||||
|
module.exit_json(changed=True, vmid=vmid, msg="Disk %s detached from VM %s" % (disk, vmid))
|
||||||
|
except Exception as e:
|
||||||
|
module.fail_json(msg="Failed to detach disk %s from VM %s with exception: %s" % (disk, vmid, str(e)))
|
||||||
|
|
||||||
|
elif state == 'moved':
|
||||||
|
try:
|
||||||
|
disk_config = disk_conf_str_to_dict(vm_config[disk])
|
||||||
|
disk_storage = disk_config["storage_name"]
|
||||||
|
if proxmox.move_disk(disk, vmid, vm, vm_config):
|
||||||
|
module.exit_json(changed=True, vmid=vmid,
|
||||||
|
msg="Disk %s moved from VM %s storage %s" % (disk, vmid, disk_storage))
|
||||||
|
else:
|
||||||
|
module.exit_json(changed=False, vmid=vmid, msg="Disk %s already at %s storage" % (disk, disk_storage))
|
||||||
|
except Exception as e:
|
||||||
|
module.fail_json(msg="Failed to move disk %s in VM %s with exception: %s" % (disk, vmid, str(e)))
|
||||||
|
|
||||||
|
elif state == 'resized':
|
||||||
|
try:
|
||||||
|
size = module.params['size']
|
||||||
|
if not match(r'^\+?\d+(\.\d+)?[KMGT]?$', size):
|
||||||
|
module.fail_json(msg="Unrecognized size pattern for disk %s: %s" % (disk, size))
|
||||||
|
disk_config = disk_conf_str_to_dict(vm_config[disk])
|
||||||
|
actual_size = disk_config['size']
|
||||||
|
if size == actual_size:
|
||||||
|
module.exit_json(changed=False, vmid=vmid, msg="Disk %s is already %s size" % (disk, size))
|
||||||
|
proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).resize.set(vmid=vmid, disk=disk, size=size)
|
||||||
|
module.exit_json(changed=True, vmid=vmid, msg="Disk %s resized in VM %s" % (disk, vmid))
|
||||||
|
except Exception as e:
|
||||||
|
module.fail_json(msg="Failed to resize disk %s in VM %s with exception: %s" % (disk, vmid, str(e)))
|
||||||
|
|
||||||
|
elif state == 'absent':
|
||||||
|
try:
|
||||||
|
if disk not in vm_config:
|
||||||
|
module.exit_json(changed=False, vmid=vmid, msg="Disk %s is already absent in VM %s" % (disk, vmid))
|
||||||
|
proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).unlink.put(vmid=vmid, idlist=disk, force=1)
|
||||||
|
module.exit_json(changed=True, vmid=vmid, msg="Disk %s removed from VM %s" % (disk, vmid))
|
||||||
|
except Exception as e:
|
||||||
|
module.fail_json(vmid=vmid, msg='Unable to remove disk %s from VM %s: %s' % (disk, vmid, str(e)))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -313,6 +313,202 @@
|
||||||
- results.vmid == {{ vmid }}
|
- results.vmid == {{ vmid }}
|
||||||
- results.msg == "Nic net5 deleted on VM with vmid {{ vmid }}"
|
- results.msg == "Nic net5 deleted on VM with vmid {{ vmid }}"
|
||||||
|
|
||||||
|
- name: Create new disk in VM
|
||||||
|
tags: ['create_disk']
|
||||||
|
block:
|
||||||
|
- name: Add new disk (without force) to VM
|
||||||
|
proxmox_disk:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_user: "{{ user }}@{{ domain }}"
|
||||||
|
api_password: "{{ api_password | default(omit) }}"
|
||||||
|
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||||
|
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||||
|
vmid: "{{ vmid }}"
|
||||||
|
disk: "{{ disk }}"
|
||||||
|
storage: "{{ storage }}"
|
||||||
|
size: 1
|
||||||
|
state: present
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- results is changed
|
||||||
|
- results.vmid == {{ vmid }}
|
||||||
|
- results.msg == "Disk {{ disk }} created in VM {{ vmid }}"
|
||||||
|
|
||||||
|
- name: Try add disk again with same options (expect no-op)
|
||||||
|
proxmox_disk:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_user: "{{ user }}@{{ domain }}"
|
||||||
|
api_password: "{{ api_password | default(omit) }}"
|
||||||
|
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||||
|
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||||
|
vmid: "{{ vmid }}"
|
||||||
|
disk: "{{ disk }}"
|
||||||
|
storage: "{{ storage }}"
|
||||||
|
size: 1
|
||||||
|
state: present
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- results is not changed
|
||||||
|
- results.vmid == {{ vmid }}
|
||||||
|
- results.msg == "Disk {{ disk }} is up to date in VM {{ vmid }}"
|
||||||
|
|
||||||
|
- name: Add new disk replacing existing disk (detach old and leave unused)
|
||||||
|
proxmox_disk:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_user: "{{ user }}@{{ domain }}"
|
||||||
|
api_password: "{{ api_password | default(omit) }}"
|
||||||
|
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||||
|
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||||
|
vmid: "{{ vmid }}"
|
||||||
|
disk: "{{ disk }}"
|
||||||
|
storage: "{{ storage }}"
|
||||||
|
size: 2
|
||||||
|
create: forced
|
||||||
|
state: present
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- results is changed
|
||||||
|
- results.vmid == {{ vmid }}
|
||||||
|
- results.msg == "Disk {{ disk }} created in VM {{ vmid }}"
|
||||||
|
|
||||||
|
- name: Update existing disk in VM
|
||||||
|
tags: ['update_disk']
|
||||||
|
block:
|
||||||
|
- name: Update disk configuration
|
||||||
|
proxmox_disk:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_user: "{{ user }}@{{ domain }}"
|
||||||
|
api_password: "{{ api_password | default(omit) }}"
|
||||||
|
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||||
|
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||||
|
vmid: "{{ vmid }}"
|
||||||
|
disk: "{{ disk }}"
|
||||||
|
backup: false
|
||||||
|
ro: true
|
||||||
|
aio: native
|
||||||
|
state: present
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- results is changed
|
||||||
|
- results.vmid == {{ vmid }}
|
||||||
|
- results.msg == "Disk {{ disk }} updated in VM {{ vmid }}"
|
||||||
|
|
||||||
|
- name: Grow existing disk in VM
|
||||||
|
tags: ['grow_disk']
|
||||||
|
block:
|
||||||
|
- name: Increase disk size
|
||||||
|
proxmox_disk:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_user: "{{ user }}@{{ domain }}"
|
||||||
|
api_password: "{{ api_password | default(omit) }}"
|
||||||
|
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||||
|
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||||
|
vmid: "{{ vmid }}"
|
||||||
|
disk: "{{ disk }}"
|
||||||
|
size: +1G
|
||||||
|
state: resized
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- results is changed
|
||||||
|
- results.vmid == {{ vmid }}
|
||||||
|
- results.msg == "Disk {{ disk }} resized in VM {{ vmid }}"
|
||||||
|
|
||||||
|
- name: Detach disk and leave it unused
|
||||||
|
tags: ['detach_disk']
|
||||||
|
block:
|
||||||
|
- name: Detach disk
|
||||||
|
proxmox_disk:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_user: "{{ user }}@{{ domain }}"
|
||||||
|
api_password: "{{ api_password | default(omit) }}"
|
||||||
|
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||||
|
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||||
|
vmid: "{{ vmid }}"
|
||||||
|
disk: "{{ disk }}"
|
||||||
|
state: detached
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- results is changed
|
||||||
|
- results.vmid == {{ vmid }}
|
||||||
|
- results.msg == "Disk {{ disk }} detached from VM {{ vmid }}"
|
||||||
|
|
||||||
|
- name: Move disk to another storage or another VM
|
||||||
|
tags: ['move_disk']
|
||||||
|
block:
|
||||||
|
- name: Move disk to another storage inside same VM
|
||||||
|
proxmox_disk:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_user: "{{ user }}@{{ domain }}"
|
||||||
|
api_password: "{{ api_password | default(omit) }}"
|
||||||
|
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||||
|
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||||
|
vmid: "{{ vmid }}"
|
||||||
|
disk: "{{ disk }}"
|
||||||
|
target_storage: "{{ target_storage }}"
|
||||||
|
format: "{{ target_format }}"
|
||||||
|
state: moved
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- results is changed
|
||||||
|
- results.vmid == {{ vmid }}
|
||||||
|
- results.msg == "Disk {{ disk }} moved from VM {{ vmid }} storage {{ results.storage }}"
|
||||||
|
|
||||||
|
- name: Move disk to another VM (same storage)
|
||||||
|
proxmox_disk:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_user: "{{ user }}@{{ domain }}"
|
||||||
|
api_password: "{{ api_password | default(omit) }}"
|
||||||
|
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||||
|
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||||
|
vmid: "{{ vmid }}"
|
||||||
|
disk: "{{ disk }}"
|
||||||
|
target_vmid: "{{ target_vm }}"
|
||||||
|
target_disk: "{{ target_disk }}"
|
||||||
|
state: moved
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- results is changed
|
||||||
|
- results.vmid == {{ vmid }}
|
||||||
|
- results.msg == "Disk {{ disk }} moved from VM {{ vmid }} storage {{ results.storage }}"
|
||||||
|
|
||||||
|
|
||||||
|
- name: Remove disk permanently
|
||||||
|
tags: ['remove_disk']
|
||||||
|
block:
|
||||||
|
- name: Remove disk
|
||||||
|
proxmox_disk:
|
||||||
|
api_host: "{{ api_host }}"
|
||||||
|
api_user: "{{ user }}@{{ domain }}"
|
||||||
|
api_password: "{{ api_password | default(omit) }}"
|
||||||
|
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||||
|
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||||
|
vmid: "{{ target_vm }}"
|
||||||
|
disk: "{{ target_disk }}"
|
||||||
|
state: absent
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- results is changed
|
||||||
|
- results.vmid == {{ target_vm }}
|
||||||
|
- results.msg == "Disk {{ target_disk }} removed from VM {{ target_vm }}"
|
||||||
|
|
||||||
- name: VM stop
|
- name: VM stop
|
||||||
tags: [ 'stop' ]
|
tags: [ 'stop' ]
|
||||||
block:
|
block:
|
||||||
|
|
Loading…
Reference in a new issue