1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Add Cloud Init variables for Proxmox_KVM module (#797) (#1380)

* Add Cloud Init variables for Proxmox_KVM module

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/modules/cloud/misc/proxmox_kvm.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* apply yaml syntax to all examples

* remove trailing whitespaces

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Amin Vakil <info@aminvakil.com>
(cherry picked from commit 1f7649fcd7)

Co-authored-by: morph027 <morphsen@gmx.com>
This commit is contained in:
patchback[bot] 2020-11-24 20:58:28 +01:00 committed by GitHub
parent d5c24e67e8
commit 999620c789
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 150 additions and 12 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- "proxmox_kvm - add cloud-init support (new options: ``cicustom``, ``cipassword``, ``citype``, ``ciuser``, ``ipconfig``, ``nameservers``, ``searchdomains``, ``sshkeys``) (https://github.com/ansible-collections/community.general/pull/797)."

View file

@ -89,6 +89,29 @@ options:
description: description:
- Enable booting from specified disk. C((ide|sata|scsi|virtio)\d+) - Enable booting from specified disk. C((ide|sata|scsi|virtio)\d+)
type: str type: str
cicustom:
description:
- 'cloud-init: Specify custom files to replace the automatically generated ones at start.'
type: str
version_added: 1.3.0
cipassword:
description:
- 'cloud-init: password of default user to create.'
type: str
version_added: 1.3.0
citype:
description:
- 'cloud-init: Specifies the cloud-init configuration format.'
- The default depends on the configured operating system type (C(ostype)).
- We use the C(nocloud) format for Linux, and C(configdrive2) for Windows.
type: str
choices: ['nocloud', 'configdrive2']
version_added: 1.3.0
ciuser:
description:
- 'cloud-init: username of default user to create.'
type: str
version_added: 1.3.0
clone: clone:
description: description:
- Name of VM to be cloned. If C(vmid) is setted, C(clone) can take arbitrary value but required for initiating the clone. - Name of VM to be cloned. If C(vmid) is setted, C(clone) can take arbitrary value but required for initiating the clone.
@ -195,6 +218,19 @@ options:
- C(size) is the size of the disk in GB. - C(size) is the size of the disk in GB.
- C(format) is the drive's backing file's data format. C(qcow2|raw|subvol). - C(format) is the drive's backing file's data format. C(qcow2|raw|subvol).
type: dict type: dict
ipconfig:
description:
- 'cloud-init: Set the IP configuration.'
- A hash/dictionary of network ip configurations. C(ipconfig='{"key":"value", "key":"value"}').
- Keys allowed are - C(ipconfig[n]) where 0 n network interfaces.
- Values allowed are - C("[gw=<GatewayIPv4>] [,gw6=<GatewayIPv6>] [,ip=<IPv4Format/CIDR>] [,ip6=<IPv6Format/CIDR>]").
- 'cloud-init: Specify IP addresses and gateways for the corresponding interface.'
- IP addresses use CIDR notation, gateways are optional but they should be in the same subnet of specified IP address.
- The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit gateway should be provided.
- For IPv6 the special string 'auto' can be used to use stateless autoconfiguration.
- If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using dhcp on IPv4.
type: dict
version_added: 1.3.0
keyboard: keyboard:
description: description:
- Sets the keyboard layout for VNC server. - Sets the keyboard layout for VNC server.
@ -242,6 +278,13 @@ options:
- Specifies the VM name. Only used on the configuration web interface. - Specifies the VM name. Only used on the configuration web interface.
- Required only for C(state=present). - Required only for C(state=present).
type: str type: str
nameservers:
description:
- 'cloud-init: DNS server IP address(es).'
- If unset, PVE host settings are used.
type: list
elements: str
version_added: 1.3.0
net: net:
description: description:
- A hash/dictionary of network interfaces for the VM. C(net='{"key":"value", "key":"value"}'). - A hash/dictionary of network interfaces for the VM. C(net='{"key":"value", "key":"value"}').
@ -339,6 +382,13 @@ options:
- Specifies the SCSI controller model. - Specifies the SCSI controller model.
type: str type: str
choices: ['lsi', 'lsi53c810', 'virtio-scsi-pci', 'virtio-scsi-single', 'megasas', 'pvscsi'] choices: ['lsi', 'lsi53c810', 'virtio-scsi-pci', 'virtio-scsi-single', 'megasas', 'pvscsi']
searchdomains:
description:
- 'cloud-init: Sets DNS search domain(s).'
- If unset, PVE host settings are used.
type: list
elements: str
version_added: 1.3.0
serial: serial:
description: description:
- A hash/dictionary of serial device to create inside the VM. C('{"key":"value", "key":"value"}'). - A hash/dictionary of serial device to create inside the VM. C('{"key":"value", "key":"value"}').
@ -373,6 +423,11 @@ options:
option has a default of C(1). Note that the default value of I(proxmox_default_behavior) option has a default of C(1). Note that the default value of I(proxmox_default_behavior)
changes in community.general 4.0.0. changes in community.general 4.0.0.
type: int type: int
sshkeys:
description:
- 'cloud-init: SSH key to assign to the default user. NOT TESTED with multiple keys but a multi-line value should work.'
type: str
version_added: 1.3.0
startdate: startdate:
description: description:
- Sets the initial date of the real time clock. - Sets the initial date of the real time clock.
@ -513,7 +568,9 @@ EXAMPLES = '''
api_host: helldorado api_host: helldorado
name: spynal name: spynal
node: sabrewulf node: sabrewulf
net: '{"net0":"virtio,bridge=vmbr1,rate=200", "net1":"e1000,bridge=vmbr2,"}' net:
net0: 'virtio,bridge=vmbr1,rate=200'
net1: 'e1000,bridge=vmbr2'
- name: Create new VM with one network interface, three virto hard disk, 4 cores, and 2 vcpus - name: Create new VM with one network interface, three virto hard disk, 4 cores, and 2 vcpus
community.general.proxmox_kvm: community.general.proxmox_kvm:
@ -522,8 +579,12 @@ EXAMPLES = '''
api_host: helldorado api_host: helldorado
name: spynal name: spynal
node: sabrewulf node: sabrewulf
net: '{"net0":"virtio,bridge=vmbr1,rate=200"}' net:
virtio: '{"virtio0":"VMs_LVM:10", "virtio1":"VMs:2,format=qcow2", "virtio2":"VMs:5,format=raw"}' net0: 'virtio,bridge=vmbr1,rate=200'
virtio:
virtio0: 'VMs_LVM:10'
virtio1: 'VMs:2,format=qcow2'
virtio2: 'VMs:5,format=raw'
cores: 4 cores: 4
vcpus: 2 vcpus: 2
@ -572,7 +633,7 @@ EXAMPLES = '''
format: raw format: raw
timeout: 300 timeout: 300
- name: Create new VM and lock it for snapashot - name: Create new VM and lock it for snapshot
community.general.proxmox_kvm: community.general.proxmox_kvm:
api_user: root@pam api_user: root@pam
api_password: secret api_password: secret
@ -590,6 +651,43 @@ EXAMPLES = '''
node: sabrewulf node: sabrewulf
protection: yes protection: yes
- name: Create new VM using cloud-init with a username and password
community.general.proxmox_kvm:
node: sabrewulf
api_user: root@pam
api_password: secret
api_host: helldorado
name: spynal
ide:
ide2: 'local:cloudinit,format=qcow2'
ciuser: mylinuxuser
cipassword: supersecret
searchdomains: 'mydomain.internal'
nameservers: 1.1.1.1
net:
net0: 'virtio,bridge=vmbr1,tag=77'
ipconfig:
ipconfig0: 'ip=192.168.1.1/24,gw=192.168.1.1'
- name: Create new VM using Cloud-Init with an ssh key
community.general.proxmox_kvm:
node: sabrewulf
api_user: root@pam
api_password: secret
api_host: helldorado
name: spynal
ide:
ide2: 'local:cloudinit,format=qcow2'
sshkeys: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILJkVm98B71lD5XHfihwcYHE9TVpsJmK1vR1JcaU82L+'
searchdomains: 'mydomain.internal'
nameservers:
- '1.1.1.1'
- '8.8.8.8'
net:
net0: 'virtio,bridge=vmbr1,tag=77'
ipconfig:
ipconfig0: 'ip=192.168.1.1/24'
- name: Start VM - name: Start VM
community.general.proxmox_kvm: community.general.proxmox_kvm:
api_user: root@pam api_user: root@pam
@ -720,6 +818,7 @@ import re
import time import time
import traceback import traceback
from distutils.version import LooseVersion from distutils.version import LooseVersion
from ansible.module_utils.six.moves.urllib.parse import quote
try: try:
from proxmoxer import ProxmoxAPI from proxmoxer import ProxmoxAPI
@ -823,6 +922,7 @@ def wait_for_task(module, proxmox, node, taskid):
def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sockets, update, **kwargs): def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sockets, update, **kwargs):
# Available only in PVE 4 # Available only in PVE 4
only_v4 = ['force', 'protection', 'skiplock'] only_v4 = ['force', 'protection', 'skiplock']
only_v6 = ['ciuser', 'cipassword', 'sshkeys', 'ipconfig']
# valide clone parameters # valide clone parameters
valid_clone_params = ['format', 'full', 'pool', 'snapname', 'storage', 'target'] valid_clone_params = ['format', 'full', 'pool', 'snapname', 'storage', 'target']
@ -836,12 +936,23 @@ def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sock
kwargs = dict((k, v) for k, v in kwargs.items() if v is not None) kwargs = dict((k, v) for k, v in kwargs.items() if v is not None)
kwargs.update(dict([k, int(v)] for k, v in kwargs.items() if isinstance(v, bool))) kwargs.update(dict([k, int(v)] for k, v in kwargs.items() if isinstance(v, bool)))
# The features work only on PVE 4 # The features work only on PVE 4+
if PVE_MAJOR_VERSION < 4: if PVE_MAJOR_VERSION < 4:
for p in only_v4: for p in only_v4:
if p in kwargs: if p in kwargs:
del kwargs[p] del kwargs[p]
# The features work only on PVE 6
if PVE_MAJOR_VERSION < 6:
for p in only_v6:
if p in kwargs:
del kwargs[p]
# 'sshkeys' param expects an urlencoded string
if 'sshkeys' in kwargs:
urlencoded_ssh_keys = quote(kwargs['sshkeys'], safe='')
kwargs['sshkeys'] = str(urlencoded_ssh_keys)
# If update, don't update disk (virtio, ide, sata, scsi) and network interface # If update, don't update disk (virtio, ide, sata, scsi) and network interface
# pool parameter not supported by qemu/<vmid>/config endpoint on "update" (PVE 6.2) - only with "create" # pool parameter not supported by qemu/<vmid>/config endpoint on "update" (PVE 6.2) - only with "create"
if update: if update:
@ -860,7 +971,7 @@ def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sock
if 'pool' in kwargs: if 'pool' in kwargs:
del kwargs['pool'] del kwargs['pool']
# Convert all dict in kwargs to elements. For hostpci[n], ide[n], net[n], numa[n], parallel[n], sata[n], scsi[n], serial[n], virtio[n] # Convert all dict in kwargs to elements. For hostpci[n], ide[n], net[n], numa[n], parallel[n], sata[n], scsi[n], serial[n], virtio[n], ipconfig[n]
for k in list(kwargs.keys()): for k in list(kwargs.keys()):
if isinstance(kwargs[k], dict): if isinstance(kwargs[k], dict):
kwargs.update(kwargs[k]) kwargs.update(kwargs[k])
@ -871,6 +982,16 @@ def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sock
kwargs['numa'] = kwargs['numa_enabled'] kwargs['numa'] = kwargs['numa_enabled']
del kwargs['numa_enabled'] del kwargs['numa_enabled']
# PVE api expects strings for the following params
if 'nameservers' in module.params:
nameservers = module.params.pop('nameservers')
if nameservers:
kwargs['nameserver'] = ' '.join(nameservers)
if 'searchdomains' in module.params:
searchdomains = module.params.pop('searchdomains')
if searchdomains:
kwargs['searchdomain'] = ' '.join(searchdomains)
# -args and skiplock require root@pam user # -args and skiplock require root@pam user
if module.params['api_user'] == "root@pam" and module.params['args'] is None: if module.params['api_user'] == "root@pam" and module.params['args'] is None:
if not update: if not update:
@ -947,6 +1068,10 @@ def main():
bios=dict(choices=['seabios', 'ovmf']), bios=dict(choices=['seabios', 'ovmf']),
boot=dict(type='str'), boot=dict(type='str'),
bootdisk=dict(type='str'), bootdisk=dict(type='str'),
cicustom=dict(type='str'),
cipassword=dict(type='str', no_log=True),
citype=dict(type='str', choices=['nocloud', 'configdrive2']),
ciuser=dict(type='str'),
clone=dict(type='str', default=None), clone=dict(type='str', default=None),
cores=dict(type='int'), cores=dict(type='int'),
cpu=dict(type='str'), cpu=dict(type='str'),
@ -963,6 +1088,7 @@ def main():
hotplug=dict(type='str'), hotplug=dict(type='str'),
hugepages=dict(choices=['any', '2', '1024']), hugepages=dict(choices=['any', '2', '1024']),
ide=dict(type='dict'), ide=dict(type='dict'),
ipconfig=dict(type='dict'),
keyboard=dict(type='str'), keyboard=dict(type='str'),
kvm=dict(type='bool'), kvm=dict(type='bool'),
localtime=dict(type='bool'), localtime=dict(type='bool'),
@ -972,6 +1098,7 @@ def main():
migrate_downtime=dict(type='int'), migrate_downtime=dict(type='int'),
migrate_speed=dict(type='int'), migrate_speed=dict(type='int'),
name=dict(type='str'), name=dict(type='str'),
nameservers=dict(type='list', elements='str'),
net=dict(type='dict'), net=dict(type='dict'),
newid=dict(type='int', default=None), newid=dict(type='int', default=None),
node=dict(), node=dict(),
@ -988,11 +1115,13 @@ def main():
scsi=dict(type='dict'), scsi=dict(type='dict'),
scsihw=dict(choices=['lsi', 'lsi53c810', 'virtio-scsi-pci', 'virtio-scsi-single', 'megasas', 'pvscsi']), scsihw=dict(choices=['lsi', 'lsi53c810', 'virtio-scsi-pci', 'virtio-scsi-single', 'megasas', 'pvscsi']),
serial=dict(type='dict'), serial=dict(type='dict'),
searchdomains=dict(type='list', elements='str'),
shares=dict(type='int'), shares=dict(type='int'),
skiplock=dict(type='bool'), skiplock=dict(type='bool'),
smbios=dict(type='str'), smbios=dict(type='str'),
snapname=dict(type='str'), snapname=dict(type='str'),
sockets=dict(type='int'), sockets=dict(type='int'),
sshkeys=dict(type='str'),
startdate=dict(type='str'), startdate=dict(type='str'),
startup=dict(), startup=dict(),
state=dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted', 'current']), state=dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted', 'current']),
@ -1078,7 +1207,7 @@ def main():
if not api_password: if not api_password:
try: try:
api_password = os.environ['PROXMOX_PASSWORD'] api_password = os.environ['PROXMOX_PASSWORD']
except KeyError as e: except KeyError:
module.fail_json(msg='You should set api_password param or use PROXMOX_PASSWORD environment variable') module.fail_json(msg='You should set api_password param or use PROXMOX_PASSWORD environment variable')
auth_args['password'] = api_password auth_args['password'] = api_password
else: else:
@ -1088,7 +1217,8 @@ def main():
try: try:
proxmox = ProxmoxAPI(api_host, verify_ssl=validate_certs, **auth_args) proxmox = ProxmoxAPI(api_host, verify_ssl=validate_certs, **auth_args)
global PVE_MAJOR_VERSION global PVE_MAJOR_VERSION
PVE_MAJOR_VERSION = 3 if proxmox_version(proxmox) < LooseVersion('4.0') else 4 version = proxmox_version(proxmox)
PVE_MAJOR_VERSION = 3 if version < LooseVersion('4.0') else version.version[0]
except Exception as e: except Exception as e:
module.fail_json(msg='authorization on proxmox cluster failed with exception: %s' % e) module.fail_json(msg='authorization on proxmox cluster failed with exception: %s' % e)
@ -1098,13 +1228,13 @@ def main():
if state == 'present' and not update and not clone and not delete and not revert: if state == 'present' and not update and not clone and not delete and not revert:
try: try:
vmid = get_nextvmid(module, proxmox) vmid = get_nextvmid(module, proxmox)
except Exception as e: except Exception:
module.fail_json(msg="Can't get the next vmid for VM {0} automatically. Ensure your cluster state is good".format(name)) module.fail_json(msg="Can't get the next vmid for VM {0} automatically. Ensure your cluster state is good".format(name))
else: else:
clone_target = clone or name clone_target = clone or name
try: try:
vmid = get_vmid(proxmox, clone_target)[0] vmid = get_vmid(proxmox, clone_target)[0]
except Exception as e: except Exception:
vmid = -1 vmid = -1
if clone is not None: if clone is not None:
@ -1112,7 +1242,7 @@ def main():
if not newid: if not newid:
try: try:
newid = get_nextvmid(module, proxmox) newid = get_nextvmid(module, proxmox)
except Exception as e: except Exception:
module.fail_json(msg="Can't get the next vmid for VM {0} automatically. Ensure your cluster state is good".format(name)) module.fail_json(msg="Can't get the next vmid for VM {0} automatically. Ensure your cluster state is good".format(name))
# Ensure source VM name exists when cloning # Ensure source VM name exists when cloning
@ -1164,6 +1294,10 @@ def main():
bios=module.params['bios'], bios=module.params['bios'],
boot=module.params['boot'], boot=module.params['boot'],
bootdisk=module.params['bootdisk'], bootdisk=module.params['bootdisk'],
cicustom=module.params['cicustom'],
cipassword=module.params['cipassword'],
citype=module.params['citype'],
ciuser=module.params['ciuser'],
cpulimit=module.params['cpulimit'], cpulimit=module.params['cpulimit'],
cpuunits=module.params['cpuunits'], cpuunits=module.params['cpuunits'],
description=module.params['description'], description=module.params['description'],
@ -1174,6 +1308,7 @@ def main():
hotplug=module.params['hotplug'], hotplug=module.params['hotplug'],
hugepages=module.params['hugepages'], hugepages=module.params['hugepages'],
ide=module.params['ide'], ide=module.params['ide'],
ipconfig=module.params['ipconfig'],
keyboard=module.params['keyboard'], keyboard=module.params['keyboard'],
kvm=module.params['kvm'], kvm=module.params['kvm'],
localtime=module.params['localtime'], localtime=module.params['localtime'],
@ -1198,6 +1333,7 @@ def main():
skiplock=module.params['skiplock'], skiplock=module.params['skiplock'],
smbios1=module.params['smbios'], smbios1=module.params['smbios'],
snapname=module.params['snapname'], snapname=module.params['snapname'],
sshkeys=module.params['sshkeys'],
startdate=module.params['startdate'], startdate=module.params['startdate'],
startup=module.params['startup'], startup=module.params['startup'],
tablet=module.params['tablet'], tablet=module.params['tablet'],