1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4# Copyright: (c) 2016, Abdoul Bah (@helldorado) <bahabdoul at gmail.com>
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10DOCUMENTATION = r'''
11---
12module: proxmox_kvm
13short_description: Management of Qemu(KVM) Virtual Machines in Proxmox VE cluster.
14description:
15  - Allows you to create/delete/stop Qemu(KVM) Virtual Machines in Proxmox VE cluster.
16  - From community.general 4.0.0 on, there will be no default values, see I(proxmox_default_behavior).
17author: "Abdoul Bah (@helldorado) <bahabdoul at gmail.com>"
18options:
19  acpi:
20    description:
21      - Specify if ACPI should be enabled/disabled.
22      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
23        option has a default of C(yes). Note that the default value of I(proxmox_default_behavior)
24        changes in community.general 4.0.0.
25    type: bool
26  agent:
27    description:
28      - Specify if the QEMU Guest Agent should be enabled/disabled.
29    type: bool
30  args:
31    description:
32      - Pass arbitrary arguments to kvm.
33      - This option is for experts only!
34      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
35        option has a default of C(-serial unix:/var/run/qemu-server/<vmid>.serial,server,nowait).
36        Note that the default value of I(proxmox_default_behavior) changes in community.general 4.0.0.
37    type: str
38  autostart:
39    description:
40      - Specify if the VM should be automatically restarted after crash (currently ignored in PVE API).
41      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
42        option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
43        changes in community.general 4.0.0.
44    type: bool
45  balloon:
46    description:
47      - Specify the amount of RAM for the VM in MB.
48      - Using zero disables the balloon driver.
49      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
50        option has a default of C(0). Note that the default value of I(proxmox_default_behavior)
51        changes in community.general 4.0.0.
52    type: int
53  bios:
54    description:
55      - Specify the BIOS implementation.
56    type: str
57    choices: ['seabios', 'ovmf']
58  boot:
59    description:
60      - Specify the boot order -> boot on floppy C(a), hard disk C(c), CD-ROM C(d), or network C(n).
61      - You can combine to set order.
62      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
63        option has a default of C(cnd). Note that the default value of I(proxmox_default_behavior)
64        changes in community.general 4.0.0.
65    type: str
66  bootdisk:
67    description:
68      - Enable booting from specified disk. C((ide|sata|scsi|virtio)\d+)
69    type: str
70  cicustom:
71    description:
72      - 'cloud-init: Specify custom files to replace the automatically generated ones at start.'
73    type: str
74    version_added: 1.3.0
75  cipassword:
76    description:
77      - 'cloud-init: password of default user to create.'
78    type: str
79    version_added: 1.3.0
80  citype:
81    description:
82      - 'cloud-init: Specifies the cloud-init configuration format.'
83      - The default depends on the configured operating system type (C(ostype)).
84      - We use the C(nocloud) format for Linux, and C(configdrive2) for Windows.
85    type: str
86    choices: ['nocloud', 'configdrive2']
87    version_added: 1.3.0
88  ciuser:
89    description:
90      - 'cloud-init: username of default user to create.'
91    type: str
92    version_added: 1.3.0
93  clone:
94    description:
95      - Name of VM to be cloned. If C(vmid) is setted, C(clone) can take arbitrary value but required for initiating the clone.
96    type: str
97  cores:
98    description:
99      - Specify number of cores per socket.
100      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
101        option has a default of C(1). Note that the default value of I(proxmox_default_behavior)
102        changes in community.general 4.0.0.
103    type: int
104  cpu:
105    description:
106      - Specify emulated CPU type.
107      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
108        option has a default of C(kvm64). Note that the default value of I(proxmox_default_behavior)
109        changes in community.general 4.0.0.
110    type: str
111  cpulimit:
112    description:
113      - Specify if CPU usage will be limited. Value 0 indicates no CPU limit.
114      - If the computer has 2 CPUs, it has total of '2' CPU time
115    type: int
116  cpuunits:
117    description:
118      - Specify CPU weight for a VM.
119      - You can disable fair-scheduler configuration by setting this to 0
120      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
121        option has a default of C(1000). Note that the default value of I(proxmox_default_behavior)
122        changes in community.general 4.0.0.
123    type: int
124  delete:
125    description:
126      - Specify a list of settings you want to delete.
127    type: str
128  description:
129    description:
130      - Specify the description for the VM. Only used on the configuration web interface.
131      - This is saved as comment inside the configuration file.
132    type: str
133  digest:
134    description:
135      - Specify if to prevent changes if current configuration file has different SHA1 digest.
136      - This can be used to prevent concurrent modifications.
137    type: str
138  force:
139    description:
140      - Allow to force stop VM.
141      - Can be used with states C(stopped), C(restarted) and C(absent).
142      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
143        option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
144        changes in community.general 4.0.0.
145    type: bool
146  format:
147    description:
148      - Target drive's backing file's data format.
149      - Used only with clone
150      - Use I(format=unspecified) and I(full=false) for a linked clone.
151      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
152        option has a default of C(qcow2). If I(proxmox_default_behavior) is set to C(no_defaults),
153        not specifying this option is equivalent to setting it to C(unspecified).
154        Note that the default value of I(proxmox_default_behavior) changes in community.general 4.0.0.
155    type: str
156    choices: [ "cloop", "cow", "qcow", "qcow2", "qed", "raw", "vmdk", "unspecified" ]
157  freeze:
158    description:
159      - Specify if PVE should freeze CPU at startup (use 'c' monitor command to start execution).
160    type: bool
161  full:
162    description:
163      - Create a full copy of all disk. This is always done when you clone a normal VM.
164      - For VM templates, we try to create a linked clone by default.
165      - Used only with clone
166    type: bool
167    default: 'yes'
168  hostpci:
169    description:
170      - Specify a hash/dictionary of map host pci devices into guest. C(hostpci='{"key":"value", "key":"value"}').
171      - Keys allowed are - C(hostpci[n]) where 0 ≤ n ≤ N.
172      - Values allowed are -  C("host="HOSTPCIID[;HOSTPCIID2...]",pcie="1|0",rombar="1|0",x-vga="1|0"").
173      - The C(host) parameter is Host PCI device pass through. HOSTPCIID syntax is C(bus:dev.func) (hexadecimal numbers).
174      - C(pcie=boolean) I(default=0) Choose the PCI-express bus (needs the q35 machine model).
175      - C(rombar=boolean) I(default=1) Specify whether or not the device's ROM will be visible in the guest's memory map.
176      - C(x-vga=boolean) I(default=0) Enable vfio-vga device support.
177      - /!\ This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
178    type: dict
179  hotplug:
180    description:
181      - Selectively enable hotplug features.
182      - This is a comma separated list of hotplug features C('network', 'disk', 'cpu', 'memory' and 'usb').
183      - Value 0 disables hotplug completely and value 1 is an alias for the default C('network,disk,usb').
184    type: str
185  hugepages:
186    description:
187      - Enable/disable hugepages memory.
188    type: str
189    choices: ['any', '2', '1024']
190  ide:
191    description:
192      - A hash/dictionary of volume used as IDE hard disk or CD-ROM. C(ide='{"key":"value", "key":"value"}').
193      - Keys allowed are - C(ide[n]) where 0 ≤ n ≤ 3.
194      - Values allowed are - C("storage:size,format=value").
195      - C(storage) is the storage identifier where to create the disk.
196      - C(size) is the size of the disk in GB.
197      - C(format) is the drive's backing file's data format. C(qcow2|raw|subvol).
198    type: dict
199  ipconfig:
200    description:
201      - 'cloud-init: Set the IP configuration.'
202      - A hash/dictionary of network ip configurations. C(ipconfig='{"key":"value", "key":"value"}').
203      - Keys allowed are - C(ipconfig[n]) where 0 ≤ n ≤ network interfaces.
204      - Values allowed are -  C("[gw=<GatewayIPv4>] [,gw6=<GatewayIPv6>] [,ip=<IPv4Format/CIDR>] [,ip6=<IPv6Format/CIDR>]").
205      - 'cloud-init: Specify IP addresses and gateways for the corresponding interface.'
206      - IP addresses use CIDR notation, gateways are optional but they should be in the same subnet of specified IP address.
207      - The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit gateway should be provided.
208      - For IPv6 the special string 'auto' can be used to use stateless autoconfiguration.
209      - If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using dhcp on IPv4.
210    type: dict
211    version_added: 1.3.0
212  keyboard:
213    description:
214      - Sets the keyboard layout for VNC server.
215    type: str
216  kvm:
217    description:
218      - Enable/disable KVM hardware virtualization.
219      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
220        option has a default of C(yes). Note that the default value of I(proxmox_default_behavior)
221        changes in community.general 4.0.0.
222    type: bool
223  localtime:
224    description:
225      - Sets the real time clock to local time.
226      - This is enabled by default if ostype indicates a Microsoft OS.
227    type: bool
228  lock:
229    description:
230      - Lock/unlock the VM.
231    type: str
232    choices: ['migrate', 'backup', 'snapshot', 'rollback']
233  machine:
234    description:
235      - Specifies the Qemu machine type.
236      - type => C((pc|pc(-i440fx)?-\d+\.\d+(\.pxe)?|q35|pc-q35-\d+\.\d+(\.pxe)?))
237    type: str
238  memory:
239    description:
240      - Memory size in MB for instance.
241      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
242        option has a default of C(512). Note that the default value of I(proxmox_default_behavior)
243        changes in community.general 4.0.0.
244    type: int
245  migrate_downtime:
246    description:
247      - Sets maximum tolerated downtime (in seconds) for migrations.
248    type: int
249  migrate_speed:
250    description:
251      - Sets maximum speed (in MB/s) for migrations.
252      - A value of 0 is no limit.
253    type: int
254  name:
255    description:
256      - Specifies the VM name. Only used on the configuration web interface.
257      - Required only for C(state=present).
258    type: str
259  nameservers:
260    description:
261      - 'cloud-init: DNS server IP address(es).'
262      - If unset, PVE host settings are used.
263    type: list
264    elements: str
265    version_added: 1.3.0
266  net:
267    description:
268      - A hash/dictionary of network interfaces for the VM. C(net='{"key":"value", "key":"value"}').
269      - Keys allowed are - C(net[n]) where 0 ≤ n ≤ N.
270      - Values allowed are - C("model="XX:XX:XX:XX:XX:XX",bridge="value",rate="value",tag="value",firewall="1|0",trunks="vlanid"").
271      - Model is one of C(e1000 e1000-82540em e1000-82544gc e1000-82545em i82551 i82557b i82559er ne2k_isa ne2k_pci pcnet rtl8139 virtio vmxnet3).
272      - C(XX:XX:XX:XX:XX:XX) should be an unique MAC address. This is automatically generated if not specified.
273      - The C(bridge) parameter can be used to automatically add the interface to a bridge device. The Proxmox VE standard bridge is called 'vmbr0'.
274      - Option C(rate) is used to limit traffic bandwidth from and to this interface. It is specified as floating point number, unit is 'Megabytes per second'.
275      - If you specify no bridge, we create a kvm 'user' (NATed) network device, which provides DHCP and DNS services.
276    type: dict
277  newid:
278    description:
279      - VMID for the clone. Used only with clone.
280      - If newid is not set, the next available VM ID will be fetched from ProxmoxAPI.
281    type: int
282  numa:
283    description:
284      - A hash/dictionaries of NUMA topology. C(numa='{"key":"value", "key":"value"}').
285      - Keys allowed are - C(numa[n]) where 0 ≤ n ≤ N.
286      - Values allowed are - C("cpu="<id[-id];...>",hostnodes="<id[-id];...>",memory="number",policy="(bind|interleave|preferred)"").
287      - C(cpus) CPUs accessing this NUMA node.
288      - C(hostnodes) Host NUMA nodes to use.
289      - C(memory) Amount of memory this NUMA node provides.
290      - C(policy) NUMA allocation policy.
291    type: dict
292  numa_enabled:
293    description:
294      - Enables NUMA.
295    type: bool
296  onboot:
297    description:
298      - Specifies whether a VM will be started during system bootup.
299      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
300        option has a default of C(yes). Note that the default value of I(proxmox_default_behavior)
301        changes in community.general 4.0.0.
302    type: bool
303  ostype:
304    description:
305      - Specifies guest operating system. This is used to enable special optimization/features for specific operating systems.
306      - The l26 is Linux 2.6/3.X Kernel.
307      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
308        option has a default of C(l26). Note that the default value of I(proxmox_default_behavior)
309        changes in community.general 4.0.0.
310    type: str
311    choices: ['other', 'wxp', 'w2k', 'w2k3', 'w2k8', 'wvista', 'win7', 'win8', 'win10', 'l24', 'l26', 'solaris']
312  parallel:
313    description:
314      - A hash/dictionary of map host parallel devices. C(parallel='{"key":"value", "key":"value"}').
315      - Keys allowed are - (parallel[n]) where 0 ≤ n ≤ 2.
316      - Values allowed are - C("/dev/parport\d+|/dev/usb/lp\d+").
317    type: dict
318  protection:
319    description:
320      - Enable/disable the protection flag of the VM. This will enable/disable the remove VM and remove disk operations.
321    type: bool
322  reboot:
323    description:
324      - Allow reboot. If set to C(yes), the VM exit on reboot.
325    type: bool
326  revert:
327    description:
328      - Revert a pending change.
329    type: str
330  sata:
331    description:
332      - A hash/dictionary of volume used as sata hard disk or CD-ROM. C(sata='{"key":"value", "key":"value"}').
333      - Keys allowed are - C(sata[n]) where 0 ≤ n ≤ 5.
334      - Values allowed are -  C("storage:size,format=value").
335      - C(storage) is the storage identifier where to create the disk.
336      - C(size) is the size of the disk in GB.
337      - C(format) is the drive's backing file's data format. C(qcow2|raw|subvol).
338    type: dict
339  scsi:
340    description:
341      - A hash/dictionary of volume used as SCSI hard disk or CD-ROM. C(scsi='{"key":"value", "key":"value"}').
342      - Keys allowed are - C(sata[n]) where 0 ≤ n ≤ 13.
343      - Values allowed are -  C("storage:size,format=value").
344      - C(storage) is the storage identifier where to create the disk.
345      - C(size) is the size of the disk in GB.
346      - C(format) is the drive's backing file's data format. C(qcow2|raw|subvol).
347    type: dict
348  scsihw:
349    description:
350      - Specifies the SCSI controller model.
351    type: str
352    choices: ['lsi', 'lsi53c810', 'virtio-scsi-pci', 'virtio-scsi-single', 'megasas', 'pvscsi']
353  searchdomains:
354    description:
355      - 'cloud-init: Sets DNS search domain(s).'
356      - If unset, PVE host settings are used.
357    type: list
358    elements: str
359    version_added: 1.3.0
360  serial:
361    description:
362      - A hash/dictionary of serial device to create inside the VM. C('{"key":"value", "key":"value"}').
363      - Keys allowed are - serial[n](str; required) where 0 ≤ n ≤ 3.
364      - Values allowed are - C((/dev/.+|socket)).
365      - /!\ If you pass through a host serial device, it is no longer possible to migrate such machines - use with special care.
366    type: dict
367  shares:
368    description:
369      - Rets amount of memory shares for auto-ballooning. (0 - 50000).
370      - The larger the number is, the more memory this VM gets.
371      - The number is relative to weights of all other running VMs.
372      - Using 0 disables auto-ballooning, this means no limit.
373    type: int
374  skiplock:
375    description:
376      - Ignore locks
377      - Only root is allowed to use this option.
378    type: bool
379  smbios:
380    description:
381      - Specifies SMBIOS type 1 fields.
382    type: str
383  snapname:
384    description:
385      - The name of the snapshot. Used only with clone.
386    type: str
387  sockets:
388    description:
389      - Sets the number of CPU sockets. (1 - N).
390      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
391        option has a default of C(1). Note that the default value of I(proxmox_default_behavior)
392        changes in community.general 4.0.0.
393    type: int
394  sshkeys:
395    description:
396      - 'cloud-init: SSH key to assign to the default user. NOT TESTED with multiple keys but a multi-line value should work.'
397    type: str
398    version_added: 1.3.0
399  startdate:
400    description:
401      - Sets the initial date of the real time clock.
402      - Valid format for date are C('now') or C('2016-09-25T16:01:21') or C('2016-09-25').
403    type: str
404  startup:
405    description:
406      - Startup and shutdown behavior. C([[order=]\d+] [,up=\d+] [,down=\d+]).
407      - Order is a non-negative number defining the general startup order.
408      - Shutdown in done with reverse ordering.
409    type: str
410  state:
411    description:
412      - Indicates desired state of the instance.
413      - If C(current), the current state of the VM will be fetched. You can access it with C(results.status)
414    type: str
415    choices: ['present', 'started', 'absent', 'stopped', 'restarted','current']
416    default: present
417  storage:
418    description:
419      - Target storage for full clone.
420    type: str
421  tablet:
422    description:
423      - Enables/disables the USB tablet device.
424      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
425        option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
426        changes in community.general 4.0.0.
427    type: bool
428  tags:
429    description:
430      - List of tags to apply to the VM instance.
431      - Tags must start with C([a-z0-9_]) followed by zero or more of the following characters C([a-z0-9_-+.]).
432      - Tags are only available in Proxmox 6+.
433    type: list
434    elements: str
435    version_added: 2.3.0
436  target:
437    description:
438      - Target node. Only allowed if the original VM is on shared storage.
439      - Used only with clone
440    type: str
441  tdf:
442    description:
443      - Enables/disables time drift fix.
444    type: bool
445  template:
446    description:
447      - Enables/disables the template.
448      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
449        option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
450        changes in community.general 4.0.0.
451    type: bool
452  timeout:
453    description:
454      - Timeout for operations.
455    type: int
456    default: 30
457  update:
458    description:
459      - If C(yes), the VM will be updated with new value.
460      - Cause of the operations of the API and security reasons, I have disabled the update of the following parameters
461      - C(net, virtio, ide, sata, scsi). Per example updating C(net) update the MAC address and C(virtio) create always new disk...
462      - Update of C(pool) is disabled. It needs an additional API endpoint not covered by this module.
463    type: bool
464    default: 'no'
465  vcpus:
466    description:
467      - Sets number of hotplugged vcpus.
468    type: int
469  vga:
470    description:
471      - Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'.
472      - If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
473        option has a default of C(std). Note that the default value of I(proxmox_default_behavior)
474        changes in community.general 4.0.0.
475    type: str
476    choices: ['std', 'cirrus', 'vmware', 'qxl', 'serial0', 'serial1', 'serial2', 'serial3', 'qxl2', 'qxl3', 'qxl4']
477  virtio:
478    description:
479      - A hash/dictionary of volume used as VIRTIO hard disk. C(virtio='{"key":"value", "key":"value"}').
480      - Keys allowed are - C(virto[n]) where 0 ≤ n ≤ 15.
481      - Values allowed are -  C("storage:size,format=value").
482      - C(storage) is the storage identifier where to create the disk.
483      - C(size) is the size of the disk in GB.
484      - C(format) is the drive's backing file's data format. C(qcow2|raw|subvol).
485    type: dict
486  watchdog:
487    description:
488      - Creates a virtual hardware watchdog device.
489    type: str
490  proxmox_default_behavior:
491    description:
492      - Various module options used to have default values. This cause problems when
493        user expects different behavior from proxmox by default or fill options which cause
494        problems when they have been set.
495      - The default value is C(compatibility), which will ensure that the default values
496        are used when the values are not explicitly specified by the user.
497      - From community.general 4.0.0 on, the default value will switch to C(no_defaults). To avoid
498        deprecation warnings, please set I(proxmox_default_behavior) to an explicit
499        value.
500      - This affects the I(acpi), I(autostart), I(balloon), I(boot), I(cores), I(cpu),
501        I(cpuunits), I(force), I(format), I(kvm), I(memory), I(onboot), I(ostype), I(sockets),
502        I(tablet), I(template), I(vga), options.
503    type: str
504    choices:
505      - compatibility
506      - no_defaults
507    version_added: "1.3.0"
508extends_documentation_fragment:
509  - community.general.proxmox.documentation
510  - community.general.proxmox.selection
511'''
512
513EXAMPLES = '''
514- name: Create new VM with minimal options
515  community.general.proxmox_kvm:
516    api_user: root@pam
517    api_password: secret
518    api_host: helldorado
519    name: spynal
520    node: sabrewulf
521
522- name: Create new VM with minimal options and given vmid
523  community.general.proxmox_kvm:
524    api_user: root@pam
525    api_password: secret
526    api_host: helldorado
527    name: spynal
528    node: sabrewulf
529    vmid: 100
530
531- name: Create new VM with two network interface options
532  community.general.proxmox_kvm:
533    api_user: root@pam
534    api_password: secret
535    api_host: helldorado
536    name: spynal
537    node: sabrewulf
538    net:
539      net0: 'virtio,bridge=vmbr1,rate=200'
540      net1: 'e1000,bridge=vmbr2'
541
542- name: Create new VM with one network interface, three virto hard disk, 4 cores, and 2 vcpus
543  community.general.proxmox_kvm:
544    api_user: root@pam
545    api_password: secret
546    api_host: helldorado
547    name: spynal
548    node: sabrewulf
549    net:
550      net0: 'virtio,bridge=vmbr1,rate=200'
551    virtio:
552      virtio0: 'VMs_LVM:10'
553      virtio1: 'VMs:2,format=qcow2'
554      virtio2: 'VMs:5,format=raw'
555    cores: 4
556    vcpus: 2
557
558- name: >
559    Clone VM with only source VM name.
560    The VM source is spynal.
561    The target VM name is zavala
562  community.general.proxmox_kvm:
563    api_user: root@pam
564    api_password: secret
565    api_host: helldorado
566    clone: spynal
567    name: zavala
568    node: sabrewulf
569    storage: VMs
570    format: qcow2
571    timeout: 500
572
573- name: >
574    Create linked clone VM with only source VM name.
575    The VM source is spynal.
576    The target VM name is zavala
577  community.general.proxmox_kvm:
578    api_user: root@pam
579    api_password: secret
580    api_host: helldorado
581    clone: spynal
582    name: zavala
583    node: sabrewulf
584    storage: VMs
585    full: no
586    format: unspecified
587    timeout: 500
588
589- name: Clone VM with source vmid and target newid and raw format
590  community.general.proxmox_kvm:
591    api_user: root@pam
592    api_password: secret
593    api_host: helldorado
594    clone: arbitrary_name
595    vmid: 108
596    newid: 152
597    name: zavala
598    node: sabrewulf
599    storage: LVM_STO
600    format: raw
601    timeout: 300
602
603- name: Create new VM and lock it for snapshot
604  community.general.proxmox_kvm:
605    api_user: root@pam
606    api_password: secret
607    api_host: helldorado
608    name: spynal
609    node: sabrewulf
610    lock: snapshot
611
612- name: Create new VM and set protection to disable the remove VM and remove disk operations
613  community.general.proxmox_kvm:
614    api_user: root@pam
615    api_password: secret
616    api_host: helldorado
617    name: spynal
618    node: sabrewulf
619    protection: yes
620
621- name: Create new VM using cloud-init with a username and password
622  community.general.proxmox_kvm:
623    node: sabrewulf
624    api_user: root@pam
625    api_password: secret
626    api_host: helldorado
627    name: spynal
628    ide:
629      ide2: 'local:cloudinit,format=qcow2'
630    ciuser: mylinuxuser
631    cipassword: supersecret
632    searchdomains: 'mydomain.internal'
633    nameservers: 1.1.1.1
634    net:
635      net0: 'virtio,bridge=vmbr1,tag=77'
636    ipconfig:
637      ipconfig0: 'ip=192.168.1.1/24,gw=192.168.1.1'
638
639- name: Create new VM using Cloud-Init with an ssh key
640  community.general.proxmox_kvm:
641    node: sabrewulf
642    api_user: root@pam
643    api_password: secret
644    api_host: helldorado
645    name: spynal
646    ide:
647      ide2: 'local:cloudinit,format=qcow2'
648    sshkeys: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILJkVm98B71lD5XHfihwcYHE9TVpsJmK1vR1JcaU82L+'
649    searchdomains: 'mydomain.internal'
650    nameservers:
651      - '1.1.1.1'
652      - '8.8.8.8'
653    net:
654      net0: 'virtio,bridge=vmbr1,tag=77'
655    ipconfig:
656      ipconfig0: 'ip=192.168.1.1/24'
657
658- name: Start VM
659  community.general.proxmox_kvm:
660    api_user: root@pam
661    api_password: secret
662    api_host: helldorado
663    name: spynal
664    node: sabrewulf
665    state: started
666
667- name: Stop VM
668  community.general.proxmox_kvm:
669    api_user: root@pam
670    api_password: secret
671    api_host: helldorado
672    name: spynal
673    node: sabrewulf
674    state: stopped
675
676- name: Stop VM with force
677  community.general.proxmox_kvm:
678    api_user: root@pam
679    api_password: secret
680    api_host: helldorado
681    name: spynal
682    node: sabrewulf
683    state: stopped
684    force: yes
685
686- name: Restart VM
687  community.general.proxmox_kvm:
688    api_user: root@pam
689    api_password: secret
690    api_host: helldorado
691    name: spynal
692    node: sabrewulf
693    state: restarted
694
695- name: Remove VM
696  community.general.proxmox_kvm:
697    api_user: root@pam
698    api_password: secret
699    api_host: helldorado
700    name: spynal
701    node: sabrewulf
702    state: absent
703
704- name: Get VM current state
705  community.general.proxmox_kvm:
706    api_user: root@pam
707    api_password: secret
708    api_host: helldorado
709    name: spynal
710    node: sabrewulf
711    state: current
712
713- name: Update VM configuration
714  community.general.proxmox_kvm:
715    api_user: root@pam
716    api_password: secret
717    api_host: helldorado
718    name: spynal
719    node: sabrewulf
720    cores: 8
721    memory: 16384
722    update: yes
723
724- name: Delete QEMU parameters
725  community.general.proxmox_kvm:
726    api_user: root@pam
727    api_password: secret
728    api_host: helldorado
729    name: spynal
730    node: sabrewulf
731    delete: 'args,template,cpulimit'
732
733- name: Revert a pending change
734  community.general.proxmox_kvm:
735    api_user: root@pam
736    api_password: secret
737    api_host: helldorado
738    name: spynal
739    node: sabrewulf
740    revert: 'template,cpulimit'
741'''
742
743RETURN = '''
744vmid:
745  description: The VM vmid.
746  returned: success
747  type: int
748  sample: 115
749status:
750  description: The current virtual machine status.
751  returned: success, not clone, not absent, not update
752  type: str
753  sample: running
754msg:
755  description: A short message
756  returned: always
757  type: str
758  sample: "VM kropta with vmid = 110 is running"
759'''
760
761import re
762import time
763import traceback
764from distutils.version import LooseVersion
765from ansible.module_utils.six.moves.urllib.parse import quote
766
767try:
768    from proxmoxer import ProxmoxAPI
769    HAS_PROXMOXER = True
770except ImportError:
771    HAS_PROXMOXER = False
772
773from ansible.module_utils.basic import AnsibleModule, env_fallback
774from ansible.module_utils.common.text.converters import to_native
775
776
777def get_nextvmid(module, proxmox):
778    try:
779        vmid = proxmox.cluster.nextid.get()
780        return vmid
781    except Exception as e:
782        module.fail_json(msg="Unable to get next vmid. Failed with exception: %s" % to_native(e),
783                         exception=traceback.format_exc())
784
785
786def get_vmid(proxmox, name):
787    return [vm['vmid'] for vm in proxmox.cluster.resources.get(type='vm') if vm.get('name') == name]
788
789
790def get_vm(proxmox, vmid):
791    return [vm for vm in proxmox.cluster.resources.get(type='vm') if vm['vmid'] == int(vmid)]
792
793
794def node_check(proxmox, node):
795    return [True for nd in proxmox.nodes.get() if nd['node'] == node]
796
797
798def get_vminfo(module, proxmox, node, vmid, **kwargs):
799    global results
800    results = {}
801    mac = {}
802    devices = {}
803    try:
804        vm = proxmox.nodes(node).qemu(vmid).config.get()
805    except Exception as e:
806        module.fail_json(msg='Getting information for VM with vmid = %s failed with exception: %s' % (vmid, e))
807
808    # Sanitize kwargs. Remove not defined args and ensure True and False converted to int.
809    kwargs = dict((k, v) for k, v in kwargs.items() if v is not None)
810
811    # Convert all dict in kwargs to elements.
812    # For hostpci[n], ide[n], net[n], numa[n], parallel[n], sata[n], scsi[n], serial[n], virtio[n]
813    for k in list(kwargs.keys()):
814        if isinstance(kwargs[k], dict):
815            kwargs.update(kwargs[k])
816            del kwargs[k]
817
818    # Split information by type
819    re_net = re.compile(r'net[0-9]')
820    re_dev = re.compile(r'(virtio|ide|scsi|sata)[0-9]')
821    for k in kwargs.keys():
822        if re_net.match(k):
823            mac[k] = parse_mac(vm[k])
824        elif re_dev.match(k):
825            devices[k] = parse_dev(vm[k])
826
827    results['mac'] = mac
828    results['devices'] = devices
829    results['vmid'] = int(vmid)
830
831
832def parse_mac(netstr):
833    return re.search('=(.*?),', netstr).group(1)
834
835
836def parse_dev(devstr):
837    return re.search('(.*?)(,|$)', devstr).group(1)
838
839
840def settings(proxmox, vmid, node, **kwargs):
841    proxmox_node = proxmox.nodes(node)
842
843    # Sanitize kwargs. Remove not defined args and ensure True and False converted to int.
844    kwargs = dict((k, v) for k, v in kwargs.items() if v is not None)
845
846    return proxmox_node.qemu(vmid).config.set(**kwargs) is None
847
848
849def wait_for_task(module, proxmox, node, taskid):
850    timeout = module.params['timeout']
851
852    while timeout:
853        task = proxmox.nodes(node).tasks(taskid).status.get()
854        if task['status'] == 'stopped' and task['exitstatus'] == 'OK':
855            # Wait an extra second as the API can be a ahead of the hypervisor
856            time.sleep(1)
857            return True
858        timeout = timeout - 1
859        if timeout == 0:
860            break
861        time.sleep(1)
862    return False
863
864
865def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sockets, update, **kwargs):
866    # Available only in PVE 4
867    only_v4 = ['force', 'protection', 'skiplock']
868    only_v6 = ['ciuser', 'cipassword', 'sshkeys', 'ipconfig', 'tags']
869
870    # valide clone parameters
871    valid_clone_params = ['format', 'full', 'pool', 'snapname', 'storage', 'target']
872    clone_params = {}
873    # Default args for vm. Note: -args option is for experts only. It allows you to pass arbitrary arguments to kvm.
874    vm_args = "-serial unix:/var/run/qemu-server/{0}.serial,server,nowait".format(vmid)
875
876    proxmox_node = proxmox.nodes(node)
877
878    # Sanitize kwargs. Remove not defined args and ensure True and False converted to int.
879    kwargs = dict((k, v) for k, v in kwargs.items() if v is not None)
880    kwargs.update(dict([k, int(v)] for k, v in kwargs.items() if isinstance(v, bool)))
881
882    # The features work only on PVE 4+
883    if PVE_MAJOR_VERSION < 4:
884        for p in only_v4:
885            if p in kwargs:
886                del kwargs[p]
887
888    # The features work only on PVE 6
889    if PVE_MAJOR_VERSION < 6:
890        for p in only_v6:
891            if p in kwargs:
892                del kwargs[p]
893
894    # 'sshkeys' param expects an urlencoded string
895    if 'sshkeys' in kwargs:
896        urlencoded_ssh_keys = quote(kwargs['sshkeys'], safe='')
897        kwargs['sshkeys'] = str(urlencoded_ssh_keys)
898
899    # If update, don't update disk (virtio, ide, sata, scsi) and network interface
900    # pool parameter not supported by qemu/<vmid>/config endpoint on "update" (PVE 6.2) - only with "create"
901    if update:
902        if 'virtio' in kwargs:
903            del kwargs['virtio']
904        if 'sata' in kwargs:
905            del kwargs['sata']
906        if 'scsi' in kwargs:
907            del kwargs['scsi']
908        if 'ide' in kwargs:
909            del kwargs['ide']
910        if 'net' in kwargs:
911            del kwargs['net']
912        if 'force' in kwargs:
913            del kwargs['force']
914        if 'pool' in kwargs:
915            del kwargs['pool']
916
917    # Convert all dict in kwargs to elements.
918    # For hostpci[n], ide[n], net[n], numa[n], parallel[n], sata[n], scsi[n], serial[n], virtio[n], ipconfig[n]
919    for k in list(kwargs.keys()):
920        if isinstance(kwargs[k], dict):
921            kwargs.update(kwargs[k])
922            del kwargs[k]
923
924    # Rename numa_enabled to numa. According the API documentation
925    if 'numa_enabled' in kwargs:
926        kwargs['numa'] = kwargs['numa_enabled']
927        del kwargs['numa_enabled']
928
929    # PVE api expects strings for the following params
930    if 'nameservers' in module.params:
931        nameservers = module.params.pop('nameservers')
932        if nameservers:
933            kwargs['nameserver'] = ' '.join(nameservers)
934    if 'searchdomains' in module.params:
935        searchdomains = module.params.pop('searchdomains')
936        if searchdomains:
937            kwargs['searchdomain'] = ' '.join(searchdomains)
938
939    # VM tags are expected to be valid and presented as a comma/semi-colon delimited string
940    if 'tags' in kwargs:
941        re_tag = re.compile(r'^[a-z0-9_][a-z0-9_\-\+\.]*$')
942        for tag in kwargs['tags']:
943            if not re_tag.match(tag):
944                module.fail_json(msg='%s is not a valid tag' % tag)
945        kwargs['tags'] = ",".join(kwargs['tags'])
946
947    # -args and skiplock require root@pam user - but can not use api tokens
948    if module.params['api_user'] == "root@pam" and module.params['args'] is None:
949        if not update and module.params['proxmox_default_behavior'] == 'compatibility':
950            kwargs['args'] = vm_args
951    elif module.params['api_user'] == "root@pam" and module.params['args'] is not None:
952        kwargs['args'] = module.params['args']
953    elif module.params['api_user'] != "root@pam" and module.params['args'] is not None:
954        module.fail_json(msg='args parameter require root@pam user. ')
955
956    if module.params['api_user'] != "root@pam" and module.params['skiplock'] is not None:
957        module.fail_json(msg='skiplock parameter require root@pam user. ')
958
959    if update:
960        if proxmox_node.qemu(vmid).config.set(name=name, memory=memory, cpu=cpu, cores=cores, sockets=sockets, **kwargs) is None:
961            return True
962        else:
963            return False
964    elif module.params['clone'] is not None:
965        for param in valid_clone_params:
966            if module.params[param] is not None:
967                clone_params[param] = module.params[param]
968        clone_params.update(dict([k, int(v)] for k, v in clone_params.items() if isinstance(v, bool)))
969        taskid = proxmox_node.qemu(vmid).clone.post(newid=newid, name=name, **clone_params)
970    else:
971        taskid = proxmox_node.qemu.create(vmid=vmid, name=name, memory=memory, cpu=cpu, cores=cores, sockets=sockets, **kwargs)
972
973    if not wait_for_task(module, proxmox, node, taskid):
974        module.fail_json(msg='Reached timeout while waiting for creating VM. Last line in task before timeout: %s' %
975                             proxmox_node.tasks(taskid).log.get()[:1])
976        return False
977    return True
978
979
980def start_vm(module, proxmox, vm):
981    vmid = vm[0]['vmid']
982    proxmox_node = proxmox.nodes(vm[0]['node'])
983    taskid = proxmox_node.qemu(vmid).status.start.post()
984    if not wait_for_task(module, proxmox, vm[0]['node'], taskid):
985        module.fail_json(msg='Reached timeout while waiting for starting VM. Last line in task before timeout: %s' %
986                         proxmox_node.tasks(taskid).log.get()[:1])
987        return False
988    return True
989
990
991def stop_vm(module, proxmox, vm, force):
992    vmid = vm[0]['vmid']
993    proxmox_node = proxmox.nodes(vm[0]['node'])
994    taskid = proxmox_node.qemu(vmid).status.shutdown.post(forceStop=(1 if force else 0))
995    if not wait_for_task(module, proxmox, vm[0]['node'], taskid):
996        module.fail_json(msg='Reached timeout while waiting for stopping VM. Last line in task before timeout: %s' %
997                         proxmox_node.tasks(taskid).log.get()[:1])
998        return False
999    return True
1000
1001
1002def proxmox_version(proxmox):
1003    apireturn = proxmox.version.get()
1004    return LooseVersion(apireturn['version'])
1005
1006
1007def main():
1008    module = AnsibleModule(
1009        argument_spec=dict(
1010            acpi=dict(type='bool'),
1011            agent=dict(type='bool'),
1012            args=dict(type='str'),
1013            api_host=dict(required=True),
1014            api_password=dict(no_log=True, fallback=(env_fallback, ['PROXMOX_PASSWORD'])),
1015            api_token_id=dict(no_log=True),
1016            api_token_secret=dict(no_log=True),
1017            api_user=dict(required=True),
1018            autostart=dict(type='bool'),
1019            balloon=dict(type='int'),
1020            bios=dict(choices=['seabios', 'ovmf']),
1021            boot=dict(type='str'),
1022            bootdisk=dict(type='str'),
1023            cicustom=dict(type='str'),
1024            cipassword=dict(type='str', no_log=True),
1025            citype=dict(type='str', choices=['nocloud', 'configdrive2']),
1026            ciuser=dict(type='str'),
1027            clone=dict(type='str'),
1028            cores=dict(type='int'),
1029            cpu=dict(type='str'),
1030            cpulimit=dict(type='int'),
1031            cpuunits=dict(type='int'),
1032            delete=dict(type='str'),
1033            description=dict(type='str'),
1034            digest=dict(type='str'),
1035            force=dict(type='bool'),
1036            format=dict(type='str', choices=['cloop', 'cow', 'qcow', 'qcow2', 'qed', 'raw', 'vmdk', 'unspecified']),
1037            freeze=dict(type='bool'),
1038            full=dict(type='bool', default=True),
1039            hostpci=dict(type='dict'),
1040            hotplug=dict(type='str'),
1041            hugepages=dict(choices=['any', '2', '1024']),
1042            ide=dict(type='dict'),
1043            ipconfig=dict(type='dict'),
1044            keyboard=dict(type='str'),
1045            kvm=dict(type='bool'),
1046            localtime=dict(type='bool'),
1047            lock=dict(choices=['migrate', 'backup', 'snapshot', 'rollback']),
1048            machine=dict(type='str'),
1049            memory=dict(type='int'),
1050            migrate_downtime=dict(type='int'),
1051            migrate_speed=dict(type='int'),
1052            name=dict(type='str'),
1053            nameservers=dict(type='list', elements='str'),
1054            net=dict(type='dict'),
1055            newid=dict(type='int'),
1056            node=dict(),
1057            numa=dict(type='dict'),
1058            numa_enabled=dict(type='bool'),
1059            onboot=dict(type='bool'),
1060            ostype=dict(choices=['other', 'wxp', 'w2k', 'w2k3', 'w2k8', 'wvista', 'win7', 'win8', 'win10', 'l24', 'l26', 'solaris']),
1061            parallel=dict(type='dict'),
1062            pool=dict(type='str'),
1063            protection=dict(type='bool'),
1064            reboot=dict(type='bool'),
1065            revert=dict(type='str'),
1066            sata=dict(type='dict'),
1067            scsi=dict(type='dict'),
1068            scsihw=dict(choices=['lsi', 'lsi53c810', 'virtio-scsi-pci', 'virtio-scsi-single', 'megasas', 'pvscsi']),
1069            serial=dict(type='dict'),
1070            searchdomains=dict(type='list', elements='str'),
1071            shares=dict(type='int'),
1072            skiplock=dict(type='bool'),
1073            smbios=dict(type='str'),
1074            snapname=dict(type='str'),
1075            sockets=dict(type='int'),
1076            sshkeys=dict(type='str', no_log=False),
1077            startdate=dict(type='str'),
1078            startup=dict(),
1079            state=dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted', 'current']),
1080            storage=dict(type='str'),
1081            tablet=dict(type='bool'),
1082            tags=dict(type='list', elements='str'),
1083            target=dict(type='str'),
1084            tdf=dict(type='bool'),
1085            template=dict(type='bool'),
1086            timeout=dict(type='int', default=30),
1087            update=dict(type='bool', default=False),
1088            validate_certs=dict(type='bool', default=False),
1089            vcpus=dict(type='int'),
1090            vga=dict(choices=['std', 'cirrus', 'vmware', 'qxl', 'serial0', 'serial1', 'serial2', 'serial3', 'qxl2', 'qxl3', 'qxl4']),
1091            virtio=dict(type='dict'),
1092            vmid=dict(type='int'),
1093            watchdog=dict(),
1094            proxmox_default_behavior=dict(type='str', choices=['compatibility', 'no_defaults']),
1095        ),
1096        mutually_exclusive=[('delete', 'revert'), ('delete', 'update'), ('revert', 'update'), ('clone', 'update'), ('clone', 'delete'), ('clone', 'revert')],
1097        required_together=[('api_token_id', 'api_token_secret')],
1098        required_one_of=[('name', 'vmid'), ('api_password', 'api_token_id')],
1099        required_if=[('state', 'present', ['node'])],
1100    )
1101
1102    if not HAS_PROXMOXER:
1103        module.fail_json(msg='proxmoxer required for this module')
1104
1105    api_host = module.params['api_host']
1106    api_password = module.params['api_password']
1107    api_token_id = module.params['api_token_id']
1108    api_token_secret = module.params['api_token_secret']
1109    api_user = module.params['api_user']
1110    clone = module.params['clone']
1111    cpu = module.params['cpu']
1112    cores = module.params['cores']
1113    delete = module.params['delete']
1114    memory = module.params['memory']
1115    name = module.params['name']
1116    newid = module.params['newid']
1117    node = module.params['node']
1118    revert = module.params['revert']
1119    sockets = module.params['sockets']
1120    state = module.params['state']
1121    update = bool(module.params['update'])
1122    vmid = module.params['vmid']
1123    validate_certs = module.params['validate_certs']
1124
1125    if module.params['proxmox_default_behavior'] is None:
1126        module.params['proxmox_default_behavior'] = 'compatibility'
1127        module.deprecate(
1128            'The proxmox_default_behavior option will change its default value from "compatibility" to '
1129            '"no_defaults" in community.general 4.0.0. To remove this warning, please specify an explicit value for it now',
1130            version='4.0.0', collection_name='community.general'
1131        )
1132    if module.params['proxmox_default_behavior'] == 'compatibility':
1133        old_default_values = dict(
1134            acpi=True,
1135            autostart=False,
1136            balloon=0,
1137            boot='cnd',
1138            cores=1,
1139            cpu='kvm64',
1140            cpuunits=1000,
1141            format='qcow2',
1142            kvm=True,
1143            memory=512,
1144            ostype='l26',
1145            sockets=1,
1146            tablet=False,
1147            template=False,
1148            vga='std',
1149        )
1150        for param, value in old_default_values.items():
1151            if module.params[param] is None:
1152                module.params[param] = value
1153
1154    if module.params['format'] == 'unspecified':
1155        module.params['format'] = None
1156
1157    auth_args = {'user': api_user}
1158    if not (api_token_id and api_token_secret):
1159        auth_args['password'] = api_password
1160    else:
1161        auth_args['token_name'] = api_token_id
1162        auth_args['token_value'] = api_token_secret
1163
1164    try:
1165        proxmox = ProxmoxAPI(api_host, verify_ssl=validate_certs, **auth_args)
1166        global PVE_MAJOR_VERSION
1167        version = proxmox_version(proxmox)
1168        PVE_MAJOR_VERSION = 3 if version < LooseVersion('4.0') else version.version[0]
1169    except Exception as e:
1170        module.fail_json(msg='authorization on proxmox cluster failed with exception: %s' % e)
1171
1172    # If vmid is not defined then retrieve its value from the vm name,
1173    # the cloned vm name or retrieve the next free VM id from ProxmoxAPI.
1174    if not vmid:
1175        if state == 'present' and not update and not clone and not delete and not revert:
1176            try:
1177                vmid = get_nextvmid(module, proxmox)
1178            except Exception:
1179                module.fail_json(msg="Can't get the next vmid for VM {0} automatically. Ensure your cluster state is good".format(name))
1180        else:
1181            clone_target = clone or name
1182            try:
1183                vmid = get_vmid(proxmox, clone_target)[0]
1184            except Exception:
1185                vmid = -1
1186
1187    if clone is not None:
1188        # If newid is not defined then retrieve the next free id from ProxmoxAPI
1189        if not newid:
1190            try:
1191                newid = get_nextvmid(module, proxmox)
1192            except Exception:
1193                module.fail_json(msg="Can't get the next vmid for VM {0} automatically. Ensure your cluster state is good".format(name))
1194
1195        # Ensure source VM name exists when cloning
1196        if -1 == vmid:
1197            module.fail_json(msg='VM with name = %s does not exist in cluster' % clone)
1198
1199        # Ensure source VM id exists when cloning
1200        if not get_vm(proxmox, vmid):
1201            module.fail_json(vmid=vmid, msg='VM with vmid = %s does not exist in cluster' % vmid)
1202
1203        # Ensure the choosen VM name doesn't already exist when cloning
1204        existing_vmid = get_vmid(proxmox, name)
1205        if existing_vmid:
1206            module.exit_json(changed=False, vmid=existing_vmid[0], msg="VM with name <%s> already exists" % name)
1207
1208        # Ensure the choosen VM id doesn't already exist when cloning
1209        if get_vm(proxmox, newid):
1210            module.exit_json(changed=False, vmid=vmid, msg="vmid %s with VM name %s already exists" % (newid, name))
1211
1212    if delete is not None:
1213        try:
1214            settings(proxmox, vmid, node, delete=delete)
1215            module.exit_json(changed=True, vmid=vmid, msg="Settings has deleted on VM {0} with vmid {1}".format(name, vmid))
1216        except Exception as e:
1217            module.fail_json(vmid=vmid, msg='Unable to delete settings on VM {0} with vmid {1}: '.format(name, vmid) + str(e))
1218
1219    if revert is not None:
1220        try:
1221            settings(proxmox, vmid, node, revert=revert)
1222            module.exit_json(changed=True, vmid=vmid, msg="Settings has reverted on VM {0} with vmid {1}".format(name, vmid))
1223        except Exception as e:
1224            module.fail_json(vmid=vmid, msg='Unable to revert settings on VM {0} with vmid {1}: Maybe is not a pending task...   '.format(name, vmid) + str(e))
1225
1226    if state == 'present':
1227        try:
1228            if get_vm(proxmox, vmid) and not (update or clone):
1229                module.exit_json(changed=False, vmid=vmid, msg="VM with vmid <%s> already exists" % vmid)
1230            elif get_vmid(proxmox, name) and not (update or clone):
1231                module.exit_json(changed=False, vmid=get_vmid(proxmox, name)[0], msg="VM with name <%s> already exists" % name)
1232            elif not (node, name):
1233                module.fail_json(msg='node, name is mandatory for creating/updating vm')
1234            elif not node_check(proxmox, node):
1235                module.fail_json(msg="node '%s' does not exist in cluster" % node)
1236
1237            create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sockets, update,
1238                      acpi=module.params['acpi'],
1239                      agent=module.params['agent'],
1240                      autostart=module.params['autostart'],
1241                      balloon=module.params['balloon'],
1242                      bios=module.params['bios'],
1243                      boot=module.params['boot'],
1244                      bootdisk=module.params['bootdisk'],
1245                      cicustom=module.params['cicustom'],
1246                      cipassword=module.params['cipassword'],
1247                      citype=module.params['citype'],
1248                      ciuser=module.params['ciuser'],
1249                      cpulimit=module.params['cpulimit'],
1250                      cpuunits=module.params['cpuunits'],
1251                      description=module.params['description'],
1252                      digest=module.params['digest'],
1253                      force=module.params['force'],
1254                      freeze=module.params['freeze'],
1255                      hostpci=module.params['hostpci'],
1256                      hotplug=module.params['hotplug'],
1257                      hugepages=module.params['hugepages'],
1258                      ide=module.params['ide'],
1259                      ipconfig=module.params['ipconfig'],
1260                      keyboard=module.params['keyboard'],
1261                      kvm=module.params['kvm'],
1262                      localtime=module.params['localtime'],
1263                      lock=module.params['lock'],
1264                      machine=module.params['machine'],
1265                      migrate_downtime=module.params['migrate_downtime'],
1266                      migrate_speed=module.params['migrate_speed'],
1267                      net=module.params['net'],
1268                      numa=module.params['numa'],
1269                      numa_enabled=module.params['numa_enabled'],
1270                      onboot=module.params['onboot'],
1271                      ostype=module.params['ostype'],
1272                      parallel=module.params['parallel'],
1273                      pool=module.params['pool'],
1274                      protection=module.params['protection'],
1275                      reboot=module.params['reboot'],
1276                      sata=module.params['sata'],
1277                      scsi=module.params['scsi'],
1278                      scsihw=module.params['scsihw'],
1279                      serial=module.params['serial'],
1280                      shares=module.params['shares'],
1281                      skiplock=module.params['skiplock'],
1282                      smbios1=module.params['smbios'],
1283                      snapname=module.params['snapname'],
1284                      sshkeys=module.params['sshkeys'],
1285                      startdate=module.params['startdate'],
1286                      startup=module.params['startup'],
1287                      tablet=module.params['tablet'],
1288                      tags=module.params['tags'],
1289                      target=module.params['target'],
1290                      tdf=module.params['tdf'],
1291                      template=module.params['template'],
1292                      vcpus=module.params['vcpus'],
1293                      vga=module.params['vga'],
1294                      virtio=module.params['virtio'],
1295                      watchdog=module.params['watchdog'])
1296
1297            if not clone:
1298                get_vminfo(module, proxmox, node, vmid,
1299                           ide=module.params['ide'],
1300                           net=module.params['net'],
1301                           sata=module.params['sata'],
1302                           scsi=module.params['scsi'],
1303                           virtio=module.params['virtio'])
1304            if update:
1305                module.exit_json(changed=True, vmid=vmid, msg="VM %s with vmid %s updated" % (name, vmid))
1306            elif clone is not None:
1307                module.exit_json(changed=True, vmid=newid, msg="VM %s with newid %s cloned from vm with vmid %s" % (name, newid, vmid))
1308            else:
1309                module.exit_json(changed=True, msg="VM %s with vmid %s deployed" % (name, vmid), **results)
1310        except Exception as e:
1311            if update:
1312                module.fail_json(vmid=vmid, msg="Unable to update vm {0} with vmid {1}=".format(name, vmid) + str(e))
1313            elif clone is not None:
1314                module.fail_json(vmid=vmid, msg="Unable to clone vm {0} from vmid {1}=".format(name, vmid) + str(e))
1315            else:
1316                module.fail_json(vmid=vmid, msg="creation of qemu VM %s with vmid %s failed with exception=%s" % (name, vmid, e))
1317
1318    elif state == 'started':
1319        status = {}
1320        try:
1321            if -1 == vmid:
1322                module.fail_json(msg='VM with name = %s does not exist in cluster' % name)
1323            vm = get_vm(proxmox, vmid)
1324            if not vm:
1325                module.fail_json(vmid=vmid, msg='VM with vmid <%s> does not exist in cluster' % vmid)
1326            status['status'] = vm[0]['status']
1327            if vm[0]['status'] == 'running':
1328                module.exit_json(changed=False, vmid=vmid, msg="VM %s is already running" % vmid, **status)
1329
1330            if start_vm(module, proxmox, vm):
1331                module.exit_json(changed=True, vmid=vmid, msg="VM %s started" % vmid, **status)
1332        except Exception as e:
1333            module.fail_json(vmid=vmid, msg="starting of VM %s failed with exception: %s" % (vmid, e), **status)
1334
1335    elif state == 'stopped':
1336        status = {}
1337        try:
1338            if -1 == vmid:
1339                module.fail_json(msg='VM with name = %s does not exist in cluster' % name)
1340
1341            vm = get_vm(proxmox, vmid)
1342            if not vm:
1343                module.fail_json(vmid=vmid, msg='VM with vmid = %s does not exist in cluster' % vmid)
1344
1345            status['status'] = vm[0]['status']
1346            if vm[0]['status'] == 'stopped':
1347                module.exit_json(changed=False, vmid=vmid, msg="VM %s is already stopped" % vmid, **status)
1348
1349            if stop_vm(module, proxmox, vm, force=module.params['force']):
1350                module.exit_json(changed=True, vmid=vmid, msg="VM %s is shutting down" % vmid, **status)
1351        except Exception as e:
1352            module.fail_json(vmid=vmid, msg="stopping of VM %s failed with exception: %s" % (vmid, e), **status)
1353
1354    elif state == 'restarted':
1355        status = {}
1356        try:
1357            if -1 == vmid:
1358                module.fail_json(msg='VM with name = %s does not exist in cluster' % name)
1359
1360            vm = get_vm(proxmox, vmid)
1361            if not vm:
1362                module.fail_json(vmid=vmid, msg='VM with vmid = %s does not exist in cluster' % vmid)
1363            status['status'] = vm[0]['status']
1364            if vm[0]['status'] == 'stopped':
1365                module.exit_json(changed=False, vmid=vmid, msg="VM %s is not running" % vmid, **status)
1366
1367            if stop_vm(module, proxmox, vm, force=module.params['force']) and start_vm(module, proxmox, vm):
1368                module.exit_json(changed=True, vmid=vmid, msg="VM %s is restarted" % vmid, **status)
1369        except Exception as e:
1370            module.fail_json(vmid=vmid, msg="restarting of VM %s failed with exception: %s" % (vmid, e), **status)
1371
1372    elif state == 'absent':
1373        status = {}
1374        try:
1375            vm = get_vm(proxmox, vmid)
1376            if not vm:
1377                module.exit_json(changed=False, vmid=vmid)
1378
1379            proxmox_node = proxmox.nodes(vm[0]['node'])
1380            status['status'] = vm[0]['status']
1381            if vm[0]['status'] == 'running':
1382                if module.params['force']:
1383                    stop_vm(module, proxmox, vm, True)
1384                else:
1385                    module.exit_json(changed=False, vmid=vmid, msg="VM %s is running. Stop it before deletion or use force=yes." % vmid)
1386            taskid = proxmox_node.qemu.delete(vmid)
1387            if not wait_for_task(module, proxmox, vm[0]['node'], taskid):
1388                module.fail_json(msg='Reached timeout while waiting for removing VM. Last line in task before timeout: %s' %
1389                                 proxmox_node.tasks(taskid).log.get()[:1])
1390            else:
1391                module.exit_json(changed=True, vmid=vmid, msg="VM %s removed" % vmid)
1392        except Exception as e:
1393            module.fail_json(msg="deletion of VM %s failed with exception: %s" % (vmid, e))
1394
1395    elif state == 'current':
1396        status = {}
1397        if -1 == vmid:
1398            module.fail_json(msg='VM with name = %s does not exist in cluster' % name)
1399        vm = get_vm(proxmox, vmid)
1400        if not vm:
1401            module.fail_json(msg='VM with vmid = %s does not exist in cluster' % vmid)
1402        if not name:
1403            name = vm[0]['name']
1404        current = proxmox.nodes(vm[0]['node']).qemu(vmid).status.current.get()['status']
1405        status['status'] = current
1406        if status:
1407            module.exit_json(changed=False, vmid=vmid, msg="VM %s with vmid = %s is %s" % (name, vmid, current), **status)
1408
1409
1410if __name__ == '__main__':
1411    main()
1412