diff options
Diffstat (limited to 'ansible_collections/community/general/plugins/modules/proxmox.py')
-rw-r--r-- | ansible_collections/community/general/plugins/modules/proxmox.py | 412 |
1 files changed, 319 insertions, 93 deletions
diff --git a/ansible_collections/community/general/plugins/modules/proxmox.py b/ansible_collections/community/general/plugins/modules/proxmox.py index 315ee601a..47f3faa4f 100644 --- a/ansible_collections/community/general/plugins/modules/proxmox.py +++ b/ansible_collections/community/general/plugins/modules/proxmox.py @@ -13,9 +13,9 @@ DOCUMENTATION = ''' module: proxmox short_description: Management of instances in Proxmox VE cluster description: - - allows you to create/delete/stop instances in Proxmox VE cluster - - Starting in Ansible 2.1, it automatically detects containerization type (lxc for PVE 4, openvz for older) - - Since community.general 4.0.0 on, there are no more default values, see I(proxmox_default_behavior). + - Allows you to create/delete/stop instances in Proxmox VE cluster. + - The module automatically detects containerization type (lxc for PVE 4, openvz for older). + - Since community.general 4.0.0 on, there are no more default values, see O(proxmox_default_behavior). attributes: check_mode: support: none @@ -29,45 +29,46 @@ options: hostname: description: - the instance hostname - - required only for C(state=present) + - required only for O(state=present) - must be unique if vmid is not passed type: str ostemplate: description: - the template for VM creating - - required only for C(state=present) + - required only for O(state=present) type: str disk: description: - This option was previously described as "hard disk size in GB for instance" however several formats describing a lxc mount are permitted. - - Older versions of Proxmox will accept a numeric value for size using the I(storage) parameter to automatically + - Older versions of Proxmox will accept a numeric value for size using the O(storage) parameter to automatically choose which storage to allocate from, however new versions enforce the C(<STORAGE>:<SIZE>) syntax. - "Additional options are available by using some combination of the following key-value pairs as a comma-delimited list C([volume=]<volume> [,acl=<1|0>] [,mountoptions=<opt[;opt...]>] [,quota=<1|0>] [,replicate=<1|0>] [,ro=<1|0>] [,shared=<1|0>] [,size=<DiskSize>])." - See U(https://pve.proxmox.com/wiki/Linux_Container) for a full description. - - This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(3). + - This option has no default unless O(proxmox_default_behavior) is set to V(compatibility); then the default is V(3). + - Should not be used in conjunction with O(storage). type: str cores: description: - Specify number of cores per socket. - - This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(1). + - This option has no default unless O(proxmox_default_behavior) is set to V(compatibility); then the default is V(1). type: int cpus: description: - numbers of allocated cpus for instance - - This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(1). + - This option has no default unless O(proxmox_default_behavior) is set to V(compatibility); then the default is V(1). type: int memory: description: - memory size in MB for instance - - This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(512). + - This option has no default unless O(proxmox_default_behavior) is set to V(compatibility); then the default is V(512). type: int swap: description: - swap memory size in MB for instance - - This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(0). + - This option has no default unless O(proxmox_default_behavior) is set to V(compatibility); then the default is V(0). type: int netif: description: @@ -80,6 +81,15 @@ options: type: list elements: str version_added: 2.0.0 + startup: + description: + - Specifies the startup order of the container. + - Use C(order=#) where C(#) is a non-negative number to define the general startup order. Shutdown in done with reverse ordering. + - Use C(up=#) where C(#) is in seconds, to specify a delay to wait before the next VM is started. + - Use C(down=#) where C(#) is in seconds, to specify a delay to wait before the next VM is stopped. + type: list + elements: str + version_added: 8.5.0 mounts: description: - specifies additional mounts (separate disks) for the container. As a hash/dictionary defining mount points @@ -91,17 +101,26 @@ options: onboot: description: - specifies whether a VM will be started during system bootup - - This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(false). + - This option has no default unless O(proxmox_default_behavior) is set to V(compatibility); then the default is V(false). type: bool storage: description: - target storage + - Should not be used in conjunction with O(disk). type: str default: 'local' + ostype: + description: + - Specifies the C(ostype) of the LXC container. + - If set to V(auto), no C(ostype) will be provided on instance creation. + choices: ['auto', 'debian', 'devuan', 'ubuntu', 'centos', 'fedora', 'opensuse', 'archlinux', 'alpine', 'gentoo', 'nixos', 'unmanaged'] + type: str + default: 'auto' + version_added: 8.1.0 cpuunits: description: - CPU weight for a VM - - This option has no default unless I(proxmox_default_behavior) is set to C(compatiblity); then the default is C(1000). + - This option has no default unless O(proxmox_default_behavior) is set to V(compatibility); then the default is V(1000). type: int nameserver: description: @@ -114,7 +133,7 @@ options: tags: description: - List of tags to apply to the container. - - Tags must start with C([a-z0-9_]) followed by zero or more of the following characters C([a-z0-9_-+.]). + - Tags must start with V([a-z0-9_]) followed by zero or more of the following characters V([a-z0-9_-+.]). - Tags are only available in Proxmox 7+. type: list elements: str @@ -124,12 +143,18 @@ options: - timeout for operations type: int default: 30 + update: + description: + - If V(true), the container will be updated with new values. + type: bool + default: false + version_added: 8.1.0 force: description: - - forcing operations - - can be used only with states C(present), C(stopped), C(restarted) - - with C(state=present) force option allow to overwrite existing container - - with states C(stopped) , C(restarted) allow to force stop instance + - Forcing operations. + - Can be used only with states V(present), V(stopped), V(restarted). + - with O(state=present) force option allow to overwrite existing container. + - with states V(stopped), V(restarted) allow to force stop instance. type: bool default: false purge: @@ -137,15 +162,16 @@ options: - Remove container from all related configurations. - For example backup jobs, replication jobs, or HA. - Related ACLs and Firewall entries will always be removed. - - Used with state C(absent). + - Used with O(state=absent). type: bool default: false version_added: 2.3.0 state: description: - Indicate desired state of the instance + - V(template) was added in community.general 8.1.0. type: str - choices: ['present', 'started', 'absent', 'stopped', 'restarted'] + choices: ['present', 'started', 'absent', 'stopped', 'restarted', 'template'] default: present pubkey: description: @@ -154,10 +180,9 @@ options: unprivileged: description: - Indicate if the container should be unprivileged. - - > - The default value for this parameter is C(false) but that is deprecated - and it will be replaced with C(true) in community.general 7.0.0. + - The default change to V(true) in community.general 7.0.0. It used to be V(false) before. type: bool + default: true description: description: - Specify the description for the container. Only used on the configuration web interface. @@ -169,15 +194,25 @@ options: - Script that will be executed during various steps in the containers lifetime. type: str version_added: '0.2.0' + timezone: + description: + - Timezone used by the container, accepts values like V(Europe/Paris). + - The special value V(host) configures the same timezone used by Proxmox host. + type: str + version_added: '7.1.0' proxmox_default_behavior: description: - As of community.general 4.0.0, various options no longer have default values. These default values caused problems when users expected different behavior from Proxmox by default or filled options which caused problems when set. - - The value C(compatibility) (default before community.general 4.0.0) will ensure that the default values - are used when the values are not explicitly specified by the user. The new default is C(no_defaults), + - The value V(compatibility) (default before community.general 4.0.0) will ensure that the default values + are used when the values are not explicitly specified by the user. The new default is V(no_defaults), which makes sure these options have no defaults. - - This affects the I(disk), I(cores), I(cpus), I(memory), I(onboot), I(swap), I(cpuunits) options. + - This affects the O(disk), O(cores), O(cpus), O(memory), O(onboot), O(swap), and O(cpuunits) options. + - > + This parameter is now B(deprecated) and it will be removed in community.general 10.0.0. + By then, the module's behavior should be to not set default values, equivalent to V(no_defaults). + If a consistent set of defaults is needed, the playbook or role should be responsible for setting it. type: str default: no_defaults choices: @@ -187,23 +222,25 @@ options: clone: description: - ID of the container to be cloned. - - I(description), I(hostname), and I(pool) will be copied from the cloned container if not specified. - - The type of clone created is defined by the I(clone_type) parameter. + - O(description), O(hostname), and O(pool) will be copied from the cloned container if not specified. + - The type of clone created is defined by the O(clone_type) parameter. - This operator is only supported for Proxmox clusters that use LXC containerization (PVE version >= 4). type: int version_added: 4.3.0 clone_type: description: - Type of the clone created. - - C(full) creates a full clone, and I(storage) must be specified. - - C(linked) creates a linked clone, and the cloned container must be a template container. - - C(opportunistic) creates a linked clone if the cloned container is a template container, and a full clone if not. - I(storage) may be specified, if not it will fall back to the default. + - V(full) creates a full clone, and O(storage) must be specified. + - V(linked) creates a linked clone, and the cloned container must be a template container. + - V(opportunistic) creates a linked clone if the cloned container is a template container, and a full clone if not. + O(storage) may be specified, if not it will fall back to the default. type: str choices: ['full', 'linked', 'opportunistic'] default: opportunistic version_added: 4.3.0 author: Sergei Antipov (@UnderGreen) +seealso: + - module: community.general.proxmox_vm_info extends_documentation_fragment: - community.general.proxmox.documentation - community.general.proxmox.selection @@ -222,6 +259,18 @@ EXAMPLES = r''' hostname: example.org ostemplate: 'local:vztmpl/ubuntu-14.04-x86_64.tar.gz' +- name: Create new container with minimal options specifying disk storage location and size + community.general.proxmox: + vmid: 100 + node: uk-mc02 + api_user: root@pam + api_password: 1q2w3e + api_host: node1 + password: 123456 + hostname: example.org + ostemplate: 'local:vztmpl/ubuntu-14.04-x86_64.tar.gz' + disk: 'local-lvm:20' + - name: Create new container with hookscript and description community.general.proxmox: vmid: 100 @@ -300,7 +349,7 @@ EXAMPLES = r''' api_host: node1 password: 123456 hostname: example.org - ostemplate: local:vztmpl/ubuntu-14.04-x86_64.tar.gz' + ostemplate: 'local:vztmpl/ubuntu-14.04-x86_64.tar.gz' mounts: '{"mp0":"local:8,mp=/mnt/test/"}' - name: Create new container with minimal options defining a cpu core limit @@ -312,9 +361,21 @@ EXAMPLES = r''' api_host: node1 password: 123456 hostname: example.org - ostemplate: local:vztmpl/ubuntu-14.04-x86_64.tar.gz' + ostemplate: 'local:vztmpl/ubuntu-14.04-x86_64.tar.gz' cores: 2 +- name: Create new container with minimal options and same timezone as proxmox host + community.general.proxmox: + vmid: 100 + node: uk-mc02 + api_user: root@pam + api_password: 1q2w3e + api_host: node1 + password: 123456 + hostname: example.org + ostemplate: 'local:vztmpl/ubuntu-14.04-x86_64.tar.gz' + timezone: host + - name: Create a new container with nesting enabled and allows the use of CIFS/NFS inside the container. community.general.proxmox: vmid: 100 @@ -324,7 +385,7 @@ EXAMPLES = r''' api_host: node1 password: 123456 hostname: example.org - ostemplate: local:vztmpl/ubuntu-14.04-x86_64.tar.gz' + ostemplate: 'local:vztmpl/ubuntu-14.04-x86_64.tar.gz' features: - nesting=1 - mount=cifs,nfs @@ -352,6 +413,16 @@ EXAMPLES = r''' hostname: clone.example.org storage: local +- name: Update container configuration + community.general.proxmox: + vmid: 100 + node: uk-mc02 + api_user: root@pam + api_password: 1q2w3e + api_host: node1 + netif: '{"net0":"name=eth0,gw=192.168.0.1,ip=192.168.0.3/24,bridge=vmbr0"}' + update: true + - name: Start container community.general.proxmox: vmid: 100 @@ -396,6 +467,23 @@ EXAMPLES = r''' api_host: node1 state: restarted +- name: Convert container to template + community.general.proxmox: + vmid: 100 + api_user: root@pam + api_password: 1q2w3e + api_host: node1 + state: template + +- name: Convert container to template (stop container if running) + community.general.proxmox: + vmid: 100 + api_user: root@pam + api_password: 1q2w3e + api_host: node1 + state: template + force: true + - name: Remove container community.general.proxmox: vmid: 100 @@ -427,27 +515,105 @@ class ProxmoxLxcAnsible(ProxmoxAnsible): """Check if the specified container is a template.""" proxmox_node = self.proxmox_api.nodes(node) config = getattr(proxmox_node, VZ_TYPE)(vmid).config.get() - return config['template'] + return config.get('template', False) + + def update_config(self, vmid, node, disk, cpus, memory, swap, **kwargs): + if VZ_TYPE != "lxc": + self.module.fail_json( + changed=False, + msg="Updating configuration is only supported for LXC enabled proxmox clusters.", + ) + + # Version limited features + minimum_version = {"tags": "6.1", "timezone": "6.3"} + proxmox_node = self.proxmox_api.nodes(node) + + pve_version = self.version() + + # Fail on unsupported features + for option, version in minimum_version.items(): + if pve_version < LooseVersion(version) and option in kwargs: + self.module.fail_json( + changed=False, + msg="Feature {option} is only supported in PVE {version}+, and you're using PVE {pve_version}".format( + option=option, version=version, pve_version=pve_version + ), + ) + + # Remove all empty kwarg entries + kwargs = dict((k, v) for k, v in kwargs.items() if v is not None) + + if cpus is not None: + kwargs["cpulimit"] = cpus + if disk is not None: + kwargs["rootfs"] = disk + if memory is not None: + kwargs["memory"] = memory + if swap is not None: + kwargs["swap"] = swap + if "netif" in kwargs: + kwargs.update(kwargs["netif"]) + del kwargs["netif"] + if "mounts" in kwargs: + kwargs.update(kwargs["mounts"]) + del kwargs["mounts"] + # LXC tags are expected to be valid and presented as a comma/semi-colon delimited string + if "tags" in kwargs: + re_tag = re.compile(r"^[a-z0-9_][a-z0-9_\-\+\.]*$") + for tag in kwargs["tags"]: + if not re_tag.match(tag): + self.module.fail_json(msg="%s is not a valid tag" % tag) + kwargs["tags"] = ",".join(kwargs["tags"]) + + # fetch the current config + current_config = getattr(proxmox_node, VZ_TYPE)(vmid).config.get() + + # compare the requested config against the current + update_config = False + for (arg, value) in kwargs.items(): + # if the arg isn't in the current config, it needs to be updated + if arg not in current_config: + update_config = True + break + # some values are lists, the order isn't always the same, so split them and compare by key + if isinstance(value, str): + current_values = current_config[arg].split(",") + requested_values = value.split(",") + for new_value in requested_values: + if new_value not in current_values: + update_config = True + break + # if it's not a list (or string) just compare the current value + else: + # some types don't match with the API, so forcing to string for comparison + if str(value) != str(current_config[arg]): + update_config = True + break + + if update_config: + getattr(proxmox_node, VZ_TYPE)(vmid).config.put(vmid=vmid, node=node, **kwargs) + else: + self.module.exit_json(changed=False, msg="Container config is already up to date") def create_instance(self, vmid, node, disk, storage, cpus, memory, swap, timeout, clone, **kwargs): # Version limited features minimum_version = { - 'tags': 7, + 'tags': '6.1', + 'timezone': '6.3' } proxmox_node = self.proxmox_api.nodes(node) # Remove all empty kwarg entries kwargs = dict((k, v) for k, v in kwargs.items() if v is not None) - version = self.version() - pve_major_version = 3 if version < LooseVersion('4.0') else version.version[0] + pve_version = self.version() # Fail on unsupported features for option, version in minimum_version.items(): - if pve_major_version < version and option in kwargs: - self.module.fail_json(changed=False, msg="Feature {option} is only supported in PVE {version}+, and you're using PVE {pve_major_version}". - format(option=option, version=version, pve_major_version=pve_major_version)) + if pve_version < LooseVersion(version) and option in kwargs: + self.module.fail_json(changed=False, msg="Feature {option} is only supported in PVE {version}+, and you're using PVE {pve_version}". + format(option=option, version=version, pve_version=pve_version)) if VZ_TYPE == 'lxc': kwargs['cpulimit'] = cpus @@ -474,6 +640,9 @@ class ProxmoxLxcAnsible(ProxmoxAnsible): self.module.fail_json(msg='%s is not a valid tag' % tag) kwargs['tags'] = ",".join(kwargs['tags']) + if kwargs.get('ostype') == 'auto': + kwargs.pop('ostype') + if clone is not None: if VZ_TYPE != 'lxc': self.module.fail_json(changed=False, msg="Clone operator is only supported for LXC enabled proxmox clusters.") @@ -523,7 +692,7 @@ class ProxmoxLxcAnsible(ProxmoxAnsible): return True timeout -= 1 if timeout == 0: - self.module.fail_json(msg='Reached timeout while waiting for creating VM. Last line in task before timeout: %s' % + self.module.fail_json(vmid=vmid, node=node, msg='Reached timeout while waiting for creating VM. Last line in task before timeout: %s' % proxmox_node.tasks(taskid).log.get()[:1]) time.sleep(1) @@ -536,7 +705,7 @@ class ProxmoxLxcAnsible(ProxmoxAnsible): return True timeout -= 1 if timeout == 0: - self.module.fail_json(msg='Reached timeout while waiting for starting VM. Last line in task before timeout: %s' % + self.module.fail_json(vmid=vmid, taskid=taskid, msg='Reached timeout while waiting for starting VM. Last line in task before timeout: %s' % self.proxmox_api.nodes(vm['node']).tasks(taskid).log.get()[:1]) time.sleep(1) @@ -552,12 +721,19 @@ class ProxmoxLxcAnsible(ProxmoxAnsible): return True timeout -= 1 if timeout == 0: - self.module.fail_json(msg='Reached timeout while waiting for stopping VM. Last line in task before timeout: %s' % + self.module.fail_json(vmid=vmid, taskid=taskid, msg='Reached timeout while waiting for stopping VM. Last line in task before timeout: %s' % self.proxmox_api.nodes(vm['node']).tasks(taskid).log.get()[:1]) time.sleep(1) return False + def convert_to_template(self, vm, vmid, timeout, force): + if getattr(self.proxmox_api.nodes(vm['node']), VZ_TYPE)(vmid).status.current.get()['status'] == 'running' and force: + self.stop_instance(vm, vmid, timeout, force) + # not sure why, but templating a container doesn't return a taskid + getattr(self.proxmox_api.nodes(vm['node']), VZ_TYPE)(vmid).template.post() + return True + def umount_instance(self, vm, vmid, timeout): taskid = getattr(self.proxmox_api.nodes(vm['node']), VZ_TYPE)(vmid).status.umount.post() while timeout: @@ -565,7 +741,7 @@ class ProxmoxLxcAnsible(ProxmoxAnsible): return True timeout -= 1 if timeout == 0: - self.module.fail_json(msg='Reached timeout while waiting for unmounting VM. Last line in task before timeout: %s' % + self.module.fail_json(vmid=vmid, taskid=taskid, msg='Reached timeout while waiting for unmounting VM. Last line in task before timeout: %s' % self.proxmox_api.nodes(vm['node']).tasks(taskid).log.get()[:1]) time.sleep(1) @@ -589,21 +765,28 @@ def main(): netif=dict(type='dict'), mounts=dict(type='dict'), ip_address=dict(), + ostype=dict(default='auto', choices=[ + 'auto', 'debian', 'devuan', 'ubuntu', 'centos', 'fedora', 'opensuse', 'archlinux', 'alpine', 'gentoo', 'nixos', 'unmanaged' + ]), onboot=dict(type='bool'), features=dict(type='list', elements='str'), + startup=dict(type='list', elements='str'), storage=dict(default='local'), cpuunits=dict(type='int'), nameserver=dict(), searchdomain=dict(), timeout=dict(type='int', default=30), + update=dict(type='bool', default=False), force=dict(type='bool', default=False), purge=dict(type='bool', default=False), - state=dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted']), + state=dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted', 'template']), pubkey=dict(type='str'), - unprivileged=dict(type='bool'), + unprivileged=dict(type='bool', default=True), description=dict(type='str'), hookscript=dict(type='str'), - proxmox_default_behavior=dict(type='str', default='no_defaults', choices=['compatibility', 'no_defaults']), + timezone=dict(type='str'), + proxmox_default_behavior=dict(type='str', default='no_defaults', choices=['compatibility', 'no_defaults'], + removed_in_version='9.0.0', removed_from_collection='community.general'), clone=dict(type='int'), clone_type=dict(default='opportunistic', choices=['full', 'linked', 'opportunistic']), tags=dict(type='list', elements='str') @@ -614,14 +797,15 @@ def main(): argument_spec=module_args, required_if=[ ('state', 'present', ['node', 'hostname']), - ('state', 'present', ('clone', 'ostemplate'), True), # Require one of clone and ostemplate. Together with mutually_exclusive this ensures that we - # either clone a container or create a new one from a template file. + # Require one of clone, ostemplate, or update. Together with mutually_exclusive this ensures that we + # either clone a container or create a new one from a template file. + ('state', 'present', ('clone', 'ostemplate', 'update'), True), ], required_together=[ ('api_token_id', 'api_token_secret') ], required_one_of=[('api_password', 'api_token_id')], - mutually_exclusive=[('clone', 'ostemplate')], # Creating a new container is done either by cloning an existing one, or based on a template. + mutually_exclusive=[('clone', 'ostemplate', 'update')], # Creating a new container is done either by cloning an existing one, or based on a template. ) proxmox = ProxmoxLxcAnsible(module) @@ -643,14 +827,6 @@ def main(): timeout = module.params['timeout'] clone = module.params['clone'] - if module.params['unprivileged'] is None: - module.params['unprivileged'] = False - module.deprecate( - 'The default value `false` for the parameter "unprivileged" is deprecated and it will be replaced with `true`', - version='7.0.0', - collection_name='community.general' - ) - if module.params['proxmox_default_behavior'] == 'compatibility': old_default_values = dict( disk="3", @@ -677,21 +853,59 @@ def main(): # Create a new container if state == 'present' and clone is None: try: - if proxmox.get_vm(vmid, ignore_missing=True) and not module.params['force']: - module.exit_json(changed=False, msg="VM with vmid = %s is already exists" % vmid) + if proxmox.get_vm(vmid, ignore_missing=True): + if module.params["update"]: + try: + proxmox.update_config(vmid, node, disk, cpus, memory, swap, + cores=module.params["cores"], + hostname=module.params["hostname"], + netif=module.params["netif"], + mounts=module.params["mounts"], + ip_address=module.params["ip_address"], + onboot=ansible_to_proxmox_bool(module.params["onboot"]), + cpuunits=module.params["cpuunits"], + nameserver=module.params["nameserver"], + searchdomain=module.params["searchdomain"], + features=",".join(module.params["features"]) + if module.params["features"] is not None + else None, + startup=",".join(module.params["startup"]) + if module.params["startup"] is not None + else None, + description=module.params["description"], + hookscript=module.params["hookscript"], + timezone=module.params["timezone"], + tags=module.params["tags"]) + module.exit_json( + changed=True, + vmid=vmid, + msg="Configured VM %s" % (vmid), + ) + except Exception as e: + module.fail_json( + vmid=vmid, + msg="Configuration of %s VM %s failed with exception: %s" + % (VZ_TYPE, vmid, e), + ) + if not module.params["force"]: + module.exit_json( + changed=False, + vmid=vmid, + msg="VM with vmid = %s is already exists" % vmid, + ) # If no vmid was passed, there cannot be another VM named 'hostname' if (not module.params['vmid'] and proxmox.get_vmid(hostname, ignore_missing=True) and not module.params['force']): vmid = proxmox.get_vmid(hostname) - module.exit_json(changed=False, msg="VM with hostname %s already exists and has ID number %s" % (hostname, vmid)) + module.exit_json(changed=False, vmid=vmid, msg="VM with hostname %s already exists and has ID number %s" % (hostname, vmid)) elif not proxmox.get_node(node): - module.fail_json(msg="node '%s' not exists in cluster" % node) + module.fail_json(vmid=vmid, msg="node '%s' not exists in cluster" % node) elif not proxmox.content_check(node, module.params['ostemplate'], template_store): - module.fail_json(msg="ostemplate '%s' not exists on node %s and storage %s" + module.fail_json(vmid=vmid, msg="ostemplate '%s' not exists on node %s and storage %s" % (module.params['ostemplate'], node, template_store)) except Exception as e: - module.fail_json(msg="Pre-creation checks of {VZ_TYPE} VM {vmid} failed with exception: {e}".format(VZ_TYPE=VZ_TYPE, vmid=vmid, e=e)) + module.fail_json(vmid=vmid, msg="Pre-creation checks of {VZ_TYPE} VM {vmid} failed with exception: {e}".format(VZ_TYPE=VZ_TYPE, vmid=vmid, e=e)) try: proxmox.create_instance(vmid, node, disk, storage, cpus, memory, swap, timeout, clone, @@ -702,6 +916,7 @@ def main(): ostemplate=module.params['ostemplate'], netif=module.params['netif'], mounts=module.params['mounts'], + ostype=module.params['ostype'], ip_address=module.params['ip_address'], onboot=ansible_to_proxmox_bool(module.params['onboot']), cpuunits=module.params['cpuunits'], @@ -710,48 +925,50 @@ def main(): force=ansible_to_proxmox_bool(module.params['force']), pubkey=module.params['pubkey'], features=",".join(module.params['features']) if module.params['features'] is not None else None, + startup=",".join(module.params['startup']) if module.params['startup'] is not None else None, unprivileged=ansible_to_proxmox_bool(module.params['unprivileged']), description=module.params['description'], hookscript=module.params['hookscript'], + timezone=module.params['timezone'], tags=module.params['tags']) - module.exit_json(changed=True, msg="Deployed VM %s from template %s" % (vmid, module.params['ostemplate'])) + module.exit_json(changed=True, vmid=vmid, msg="Deployed VM %s from template %s" % (vmid, module.params['ostemplate'])) except Exception as e: - module.fail_json(msg="Creation of %s VM %s failed with exception: %s" % (VZ_TYPE, vmid, e)) + module.fail_json(vmid=vmid, msg="Creation of %s VM %s failed with exception: %s" % (VZ_TYPE, vmid, e)) # Clone a container elif state == 'present' and clone is not None: try: if proxmox.get_vm(vmid, ignore_missing=True) and not module.params['force']: - module.exit_json(changed=False, msg="VM with vmid = %s is already exists" % vmid) + module.exit_json(changed=False, vmid=vmid, msg="VM with vmid = %s is already exists" % vmid) # If no vmid was passed, there cannot be another VM named 'hostname' if (not module.params['vmid'] and proxmox.get_vmid(hostname, ignore_missing=True) and not module.params['force']): vmid = proxmox.get_vmid(hostname) - module.exit_json(changed=False, msg="VM with hostname %s already exists and has ID number %s" % (hostname, vmid)) + module.exit_json(changed=False, vmid=vmid, msg="VM with hostname %s already exists and has ID number %s" % (hostname, vmid)) if not proxmox.get_vm(clone, ignore_missing=True): - module.exit_json(changed=False, msg="Container to be cloned does not exist") + module.exit_json(changed=False, vmid=vmid, msg="Container to be cloned does not exist") except Exception as e: - module.fail_json(msg="Pre-clone checks of {VZ_TYPE} VM {vmid} failed with exception: {e}".format(VZ_TYPE=VZ_TYPE, vmid=vmid, e=e)) + module.fail_json(vmid=vmid, msg="Pre-clone checks of {VZ_TYPE} VM {vmid} failed with exception: {e}".format(VZ_TYPE=VZ_TYPE, vmid=vmid, e=e)) try: proxmox.create_instance(vmid, node, disk, storage, cpus, memory, swap, timeout, clone) - module.exit_json(changed=True, msg="Cloned VM %s from %s" % (vmid, clone)) + module.exit_json(changed=True, vmid=vmid, msg="Cloned VM %s from %s" % (vmid, clone)) except Exception as e: - module.fail_json(msg="Cloning %s VM %s failed with exception: %s" % (VZ_TYPE, vmid, e)) + module.fail_json(vmid=vmid, msg="Cloning %s VM %s failed with exception: %s" % (VZ_TYPE, vmid, e)) elif state == 'started': try: vm = proxmox.get_vm(vmid) if getattr(proxmox.proxmox_api.nodes(vm['node']), VZ_TYPE)(vmid).status.current.get()['status'] == 'running': - module.exit_json(changed=False, msg="VM %s is already running" % vmid) + module.exit_json(changed=False, vmid=vmid, msg="VM %s is already running" % vmid) if proxmox.start_instance(vm, vmid, timeout): - module.exit_json(changed=True, msg="VM %s started" % vmid) + module.exit_json(changed=True, vmid=vmid, msg="VM %s started" % vmid) except Exception as e: - module.fail_json(msg="starting of VM %s failed with exception: %s" % (vmid, e)) + module.fail_json(vmid=vmid, msg="starting of VM %s failed with exception: %s" % (vmid, e)) elif state == 'stopped': try: @@ -760,18 +977,27 @@ def main(): if getattr(proxmox.proxmox_api.nodes(vm['node']), VZ_TYPE)(vmid).status.current.get()['status'] == 'mounted': if module.params['force']: if proxmox.umount_instance(vm, vmid, timeout): - module.exit_json(changed=True, msg="VM %s is shutting down" % vmid) + module.exit_json(changed=True, vmid=vmid, msg="VM %s is shutting down" % vmid) else: - module.exit_json(changed=False, msg=("VM %s is already shutdown, but mounted. " - "You can use force option to umount it.") % vmid) + module.exit_json(changed=False, vmid=vmid, + msg=("VM %s is already shutdown, but mounted. You can use force option to umount it.") % vmid) if getattr(proxmox.proxmox_api.nodes(vm['node']), VZ_TYPE)(vmid).status.current.get()['status'] == 'stopped': - module.exit_json(changed=False, msg="VM %s is already shutdown" % vmid) + module.exit_json(changed=False, vmid=vmid, msg="VM %s is already shutdown" % vmid) if proxmox.stop_instance(vm, vmid, timeout, force=module.params['force']): - module.exit_json(changed=True, msg="VM %s is shutting down" % vmid) + module.exit_json(changed=True, vmid=vmid, msg="VM %s is shutting down" % vmid) + except Exception as e: + module.fail_json(vmid=vmid, msg="stopping of VM %s failed with exception: %s" % (vmid, e)) + + elif state == 'template': + try: + vm = proxmox.get_vm(vmid) + + proxmox.convert_to_template(vm, vmid, timeout, force=module.params['force']) + module.exit_json(changed=True, msg="VM %s is converted to template" % vmid) except Exception as e: - module.fail_json(msg="stopping of VM %s failed with exception: %s" % (vmid, e)) + module.fail_json(vmid=vmid, msg="conversion of VM %s to template failed with exception: %s" % (vmid, e)) elif state == 'restarted': try: @@ -779,28 +1005,28 @@ def main(): vm_status = getattr(proxmox.proxmox_api.nodes(vm['node']), VZ_TYPE)(vmid).status.current.get()['status'] if vm_status in ['stopped', 'mounted']: - module.exit_json(changed=False, msg="VM %s is not running" % vmid) + module.exit_json(changed=False, vmid=vmid, msg="VM %s is not running" % vmid) if (proxmox.stop_instance(vm, vmid, timeout, force=module.params['force']) and proxmox.start_instance(vm, vmid, timeout)): - module.exit_json(changed=True, msg="VM %s is restarted" % vmid) + module.exit_json(changed=True, vmid=vmid, msg="VM %s is restarted" % vmid) except Exception as e: - module.fail_json(msg="restarting of VM %s failed with exception: %s" % (vmid, e)) + module.fail_json(vmid=vmid, msg="restarting of VM %s failed with exception: %s" % (vmid, e)) elif state == 'absent': if not vmid: - module.exit_json(changed=False, msg='VM with hostname = %s is already absent' % hostname) + module.exit_json(changed=False, vmid=vmid, msg='VM with hostname = %s is already absent' % hostname) try: vm = proxmox.get_vm(vmid, ignore_missing=True) if not vm: - module.exit_json(changed=False, msg="VM %s does not exist" % vmid) + module.exit_json(changed=False, vmid=vmid, msg="VM %s does not exist" % vmid) vm_status = getattr(proxmox.proxmox_api.nodes(vm['node']), VZ_TYPE)(vmid).status.current.get()['status'] if vm_status == 'running': - module.exit_json(changed=False, msg="VM %s is running. Stop it before deletion." % vmid) + module.exit_json(changed=False, vmid=vmid, msg="VM %s is running. Stop it before deletion." % vmid) if vm_status == 'mounted': - module.exit_json(changed=False, msg="VM %s is mounted. Stop it with force option before deletion." % vmid) + module.exit_json(changed=False, vmid=vmid, msg="VM %s is mounted. Stop it with force option before deletion." % vmid) delete_params = {} @@ -811,15 +1037,15 @@ def main(): while timeout: if proxmox.api_task_ok(vm['node'], taskid): - module.exit_json(changed=True, msg="VM %s removed" % vmid) + module.exit_json(changed=True, vmid=vmid, taskid=taskid, msg="VM %s removed" % vmid) timeout -= 1 if timeout == 0: - module.fail_json(msg='Reached timeout while waiting for removing VM. Last line in task before timeout: %s' + module.fail_json(vmid=vmid, taskid=taskid, msg='Reached timeout while waiting for removing VM. Last line in task before timeout: %s' % proxmox.proxmox_api.nodes(vm['node']).tasks(taskid).log.get()[:1]) time.sleep(1) except Exception as e: - module.fail_json(msg="deletion of VM %s failed with exception: %s" % (vmid, to_native(e))) + module.fail_json(vmid=vmid, msg="deletion of VM %s failed with exception: %s" % (vmid, to_native(e))) if __name__ == '__main__': |