summaryrefslogtreecommitdiff
path: root/cloud/ovirt
diff options
context:
space:
mode:
authorOndra Machacek <machacek.ondra@gmail.com>2016-09-21 18:48:05 +0200
committerRyan Brown <sb@ryansb.com>2016-09-21 12:48:05 -0400
commit87949afbfaaa7bb9ac74b0c19f43e54e0a71cf13 (patch)
treed9849163d17c90e4ef7595a897c12112923b4e2c /cloud/ovirt
parentdd8c01f8ca01181ee6eda7600b13bd52403ddc29 (diff)
downloadansible-modules-extras-87949afbfaaa7bb9ac74b0c19f43e54e0a71cf13.tar.gz
Ovirt auth/vms/disks modules (#2836)
* Add oVirt module to manage VMs This patch add oVirt module to manage Virtual Machines * Add oVirt module to manage authentication This patch add oVirt module to manage authentication * Add oVirt module to manage disks * Added VM state management and fixups
Diffstat (limited to 'cloud/ovirt')
-rw-r--r--cloud/ovirt/__init__.py0
-rw-r--r--cloud/ovirt/ovirt_auth.py233
-rw-r--r--cloud/ovirt/ovirt_disks.py316
-rw-r--r--cloud/ovirt/ovirt_vms.py804
4 files changed, 1353 insertions, 0 deletions
diff --git a/cloud/ovirt/__init__.py b/cloud/ovirt/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/cloud/ovirt/__init__.py
diff --git a/cloud/ovirt/ovirt_auth.py b/cloud/ovirt/ovirt_auth.py
new file mode 100644
index 00000000..22abf1d8
--- /dev/null
+++ b/cloud/ovirt/ovirt_auth.py
@@ -0,0 +1,233 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016 Red Hat, Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+try:
+ import ovirtsdk4 as sdk
+except ImportError:
+ pass
+
+
+DOCUMENTATION = '''
+---
+module: ovirt_auth
+short_description: "Module to manage authentication to oVirt."
+author: "Ondra Machacek (@machacekondra)"
+version_added: "2.2"
+description:
+ - "This module authenticates to oVirt engine and creates SSO token, which should be later used in
+ all other oVirt modules, so all modules don't need to perform login and logout.
+ This module returns an Ansible fact called I(ovirt_auth). Every module can use this
+ fact as C(auth) parameter, to perform authentication."
+options:
+ state:
+ default: present
+ choices: ['present', 'absent']
+ description:
+ - "Specifies if a token should be created or revoked."
+ username:
+ required: True
+ description:
+ - "The name of the user. For example: I(admin@internal)."
+ password:
+ required: True
+ description:
+ - "The password of the user."
+ url:
+ required: True
+ description:
+ - "A string containing the base URL of the server.
+ For example: I(https://server.example.com/ovirt-engine/api)."
+ insecure:
+ required: False
+ description:
+ - "A boolean flag that indicates if the server TLS certificate and host name should be checked."
+ ca_file:
+ required: False
+ description:
+ - "A PEM file containing the trusted CA certificates. The
+ certificate presented by the server will be verified using these CA
+ certificates. If C(ca_file) parameter is not set, system wide
+ CA certificate store is used."
+ timeout:
+ required: False
+ description:
+ - "The maximum total time to wait for the response, in
+ seconds. A value of zero (the default) means wait forever. If
+ the timeout expires before the response is received an exception
+ will be raised."
+ compress:
+ required: False
+ description:
+ - "A boolean flag indicating if the SDK should ask
+ the server to send compressed responses. The default is I(True).
+ Note that this is a hint for the server, and that it may return
+ uncompressed data even when this parameter is set to I(True)."
+ kerberos:
+ required: False
+ description:
+ - "A boolean flag indicating if Kerberos authentication
+ should be used instead of the default basic authentication."
+notes:
+ - "Everytime you use ovirt_auth module to obtain ticket, you need to also revoke the ticket,
+ when you no longer need it, otherwise the ticket would be revoked by engine when it expires.
+ For an example of how to achieve that, please take a look at I(examples) section."
+'''
+
+EXAMPLES = '''
+tasks:
+ - block:
+ # Create a vault with `ovirt_password` variable which store your
+ # oVirt user's password, and include that yaml file with variable:
+ - include_vars: ovirt_password.yml
+
+ # Always be sure to pass 'no_log: true' to ovirt_auth task,
+ # so the oVirt user's password is not logged:
+ - name: Obtain SSO token with using username/password credentials:
+ no_log: true
+ ovirt_auth:
+ url: https://ovirt.example.com/ovirt-engine/api
+ username: admin@internal
+ ca_file: ca.pem
+ password: "{{ ovirt_password }}"
+
+ # Previous task generated I(ovirt_auth) fact, which you can later use
+ # in different modules as follows:
+ - ovirt_vms:
+ auth: "{{ ovirt_auth }}"
+ state: absent
+ name: myvm
+
+ always:
+ - name: Always revoke the SSO token
+ ovirt_auth:
+ state: absent
+ ovirt_auth: {{ ovirt_auth }}
+'''
+
+RETURN = '''
+ovirt_auth:
+ description: Authentication facts, needed to perform authentication to oVirt.
+ returned: success
+ type: dictionary
+ contains:
+ token:
+ description: SSO token which is used for connection to oVirt engine.
+ returned: success
+ type: string
+ sample: "kdfVWp9ZgeewBXV-iq3Js1-xQJZPSEQ334FLb3eksoEPRaab07DhZ8ED8ghz9lJd-MQ2GqtRIeqhvhCkrUWQPw"
+ url:
+ description: URL of the oVirt engine API endpoint.
+ returned: success
+ type: string
+ sample: "https://ovirt.example.com/ovirt-engine/api"
+ ca_file:
+ description: CA file, which is used to verify SSL/TLS connection.
+ returned: success
+ type: string
+ sample: "ca.pem"
+ insecure:
+ description: Flag indicating if insecure connection is used.
+ returned: success
+ type: bool
+ sample: False
+ timeout:
+ description: Number of seconds to wait for response.
+ returned: success
+ type: int
+ sample: 0
+ compress:
+ description: Flag indicating if compression is used for connection.
+ returned: success
+ type: bool
+ sample: True
+ kerberos:
+ description: Flag indicating if kerberos is used for authentication.
+ returned: success
+ type: bool
+ sample: False
+'''
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ url=dict(default=None),
+ username=dict(default=None),
+ password=dict(default=None),
+ ca_file=dict(default=None),
+ insecure=dict(required=False, type='bool', default=False),
+ timeout=dict(required=False, type='int', default=0),
+ compress=dict(required=False, type='bool', default=True),
+ kerberos=dict(required=False, type='bool', default=False),
+ state=dict(default='present', choices=['present', 'absent']),
+ ovirt_auth=dict(required=None, type='dict'),
+ ),
+ required_if=[
+ ('state', 'absent', ['ovirt_auth']),
+ ('state', 'present', ['username', 'password', 'url']),
+ ],
+ )
+ check_sdk(module)
+
+ state = module.params.get('state')
+ if state == 'present':
+ params = module.params
+ elif state == 'absent':
+ params = module.params['ovirt_auth']
+
+ connection = sdk.Connection(
+ url=params.get('url'),
+ username=params.get('username'),
+ password=params.get('password'),
+ ca_file=params.get('ca_file'),
+ insecure=params.get('insecure'),
+ timeout=params.get('timeout'),
+ compress=params.get('compress'),
+ kerberos=params.get('kerberos'),
+ token=params.get('token'),
+ )
+ try:
+ token = connection.authenticate()
+ module.exit_json(
+ changed=False,
+ ansible_facts=dict(
+ ovirt_auth=dict(
+ token=token,
+ url=params.get('url'),
+ ca_file=params.get('ca_file'),
+ insecure=params.get('insecure'),
+ timeout=params.get('timeout'),
+ compress=params.get('compress'),
+ kerberos=params.get('kerberos'),
+ ) if state == 'present' else dict()
+ )
+ )
+ except Exception as e:
+ module.fail_json(msg="Error: %s" % e)
+ finally:
+ # Close the connection, but don't revoke token
+ connection.close(logout=state == 'absent')
+
+
+from ansible.module_utils.basic import *
+from ansible.module_utils.ovirt import *
+if __name__ == "__main__":
+ main()
diff --git a/cloud/ovirt/ovirt_disks.py b/cloud/ovirt/ovirt_disks.py
new file mode 100644
index 00000000..a8f84c26
--- /dev/null
+++ b/cloud/ovirt/ovirt_disks.py
@@ -0,0 +1,316 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016 Red Hat, Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+try:
+ import ovirtsdk4 as sdk
+ import ovirtsdk4.types as otypes
+except ImportError:
+ pass
+
+from ansible.module_utils.ovirt import *
+
+
+DOCUMENTATION = '''
+---
+module: ovirt_disks
+short_description: "Module to manage Virtual Machine and floating disks in oVirt."
+version_added: "2.2"
+author: "Ondra Machacek (@machacekondra)"
+description:
+ - "Module to manage Virtual Machine and floating disks in oVirt."
+options:
+ id:
+ description:
+ - "ID of the disk to manage. Either C(id) or C(name) is required."
+ name:
+ description:
+ - "Name of the disk to manage. Either C(id) or C(name)/C(alias) is required."
+ aliases: ['alias']
+ vm_name:
+ description:
+ - "Name of the Virtual Machine to manage. Either C(vm_id) or C(vm_name) is required if C(state) is I(attached) or I(detached)."
+ vm_id:
+ description:
+ - "ID of the Virtual Machine to manage. Either C(vm_id) or C(vm_name) is required if C(state) is I(attached) or I(detached)."
+ state:
+ description:
+ - "Should the Virtual Machine disk be present/absent/attached/detached."
+ choices: ['present', 'absent', 'attached', 'detached']
+ default: 'present'
+ size:
+ description:
+ - "Size of the disk. Size should be specified using IEC standard units. For example 10GiB, 1024MiB, etc."
+ interface:
+ description:
+ - "Driver of the storage interface."
+ choices: ['virtio', 'ide', 'virtio_scsi']
+ default: 'virtio'
+ format:
+ description:
+ - "Format of the disk. Either copy-on-write or raw."
+ choices: ['raw', 'cow']
+ storage_domain:
+ description:
+ - "Storage domain name where disk should be created. By default storage is chosen by oVirt engine."
+ profile:
+ description:
+ - "Disk profile name to be attached to disk. By default profile is chosen by oVirt engine."
+ bootable:
+ description:
+ - "I(True) if the disk should be bootable. By default when disk is created it isn't bootable."
+ shareable:
+ description:
+ - "I(True) if the disk should be shareable. By default when disk is created it isn't shareable."
+ logical_unit:
+ description:
+ - "Dictionary which describes LUN to be directly attached to VM:"
+ - "C(address) - Address of the storage server. Used by iSCSI."
+ - "C(port) - Port of the storage server. Used by iSCSI."
+ - "C(target) - iSCSI target."
+ - "C(lun_id) - LUN id."
+ - "C(username) - CHAP Username to be used to access storage server. Used by iSCSI."
+ - "C(password) - CHAP Password of the user to be used to access storage server. Used by iSCSI."
+ - "C(storage_type) - Storage type either I(fcp) or I(iscsi)."
+extends_documentation_fragment: ovirt
+'''
+
+
+EXAMPLES = '''
+# Examples don't contain auth parameter for simplicity,
+# look at ovirt_auth module to see how to reuse authentication:
+
+# Create and attach new disk to VM
+- ovirt_disks:
+ name: myvm_disk
+ vm_name: rhel7
+ size: 10GiB
+ format: cow
+ interface: virtio
+
+# Attach logical unit to VM rhel7
+- ovirt_disks:
+ vm_name: rhel7
+ logical_unit:
+ target: iqn.2016-08-09.brq.str-01:omachace
+ id: 1IET_000d0001
+ address: 10.34.63.204
+ interface: virtio
+
+# Detach disk from VM
+- ovirt_disks:
+ state: detached
+ name: myvm_disk
+ vm_name: rhel7
+ size: 10GiB
+ format: cow
+ interface: virtio
+'''
+
+
+RETURN = '''
+id:
+ description: "ID of the managed disk"
+ returned: "On success if disk is found."
+ type: str
+ sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
+disk:
+ description: "Dictionary of all the disk attributes. Disk attributes can be found on your oVirt instance
+ at following url: https://ovirt.example.com/ovirt-engine/api/model#types/disk."
+ returned: "On success if disk is found and C(vm_id) or C(vm_name) wasn't passed."
+
+disk_attachment:
+ description: "Dictionary of all the disk attachment attributes. Disk attachment attributes can be found
+ on your oVirt instance at following url:
+ https://ovirt.example.com/ovirt-engine/api/model#types/disk_attachment."
+ returned: "On success if disk is found and C(vm_id) or C(vm_name) was passed and VM was found."
+'''
+
+
+
+def _search_by_lun(disks_service, lun_id):
+ """
+ Find disk by LUN ID.
+ """
+ res = [
+ disk for disk in disks_service.list(search='disk_type=lun') if (
+ disk.lun_storage.id == lun_id
+ )
+ ]
+ return res[0] if res else None
+
+
+class DisksModule(BaseModule):
+
+ def build_entity(self):
+ logical_unit = self._module.params.get('logical_unit')
+ return otypes.Disk(
+ id=self._module.params.get('id'),
+ name=self._module.params.get('name'),
+ description=self._module.params.get('description'),
+ format=otypes.DiskFormat(
+ self._module.params.get('format')
+ ) if self._module.params.get('format') else None,
+ provisioned_size=convert_to_bytes(
+ self._module.params.get('size')
+ ),
+ storage_domains=[
+ otypes.StorageDomain(
+ name=self._module.params.get('storage_domain'),
+ ),
+ ],
+ shareable=self._module.params.get('shareable'),
+ lun_storage=otypes.HostStorage(
+ type=otypes.StorageType(
+ logical_unit.get('storage_type', 'iscsi')
+ ),
+ logical_units=[
+ otypes.LogicalUnit(
+ address=logical_unit.get('address'),
+ port=logical_unit.get('port', 3260),
+ target=logical_unit.get('target'),
+ id=logical_unit.get('id'),
+ username=logical_unit.get('username'),
+ password=logical_unit.get('password'),
+ )
+ ],
+ ) if logical_unit else None,
+ )
+
+ def update_check(self, entity):
+ return (
+ equal(self._module.params.get('description'), entity.description) and
+ equal(convert_to_bytes(self._module.params.get('size')), entity.provisioned_size) and
+ equal(self._module.params.get('format'), str(entity.format)) and
+ equal(self._module.params.get('shareable'), entity.shareable)
+ )
+
+
+class DiskAttachmentsModule(DisksModule):
+
+ def build_entity(self):
+ return otypes.DiskAttachment(
+ disk=super(DiskAttachmentsModule, self).build_entity(),
+ interface=otypes.DiskInterface(
+ self._module.params.get('interface')
+ ) if self._module.params.get('interface') else None,
+ bootable=self._module.params.get('bootable'),
+ active=True,
+ )
+
+ def update_check(self, entity):
+ return (
+ equal(self._module.params.get('interface'), str(entity.interface)) and
+ equal(self._module.params.get('bootable'), entity.bootable)
+ )
+
+
+def main():
+ argument_spec = ovirt_full_argument_spec(
+ state=dict(
+ choices=['present', 'absent', 'attached', 'detached'],
+ default='present'
+ ),
+ id=dict(default=None),
+ name=dict(default=None, aliases=['alias']),
+ vm_name=dict(default=None),
+ vm_id=dict(default=None),
+ size=dict(default=None),
+ interface=dict(default=None,),
+ allocation_policy=dict(default=None),
+ storage_domain=dict(default=None),
+ profile=dict(default=None),
+ format=dict(default=None, choices=['raw', 'cow']),
+ bootable=dict(default=None, type='bool'),
+ shareable=dict(default=None, type='bool'),
+ logical_unit=dict(default=None, type='dict'),
+ )
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+ check_sdk(module)
+ check_params(module)
+
+ try:
+ disk = None
+ state = module.params['state']
+ connection = create_connection(module.params.pop('auth'))
+ disks_service = connection.system_service().disks_service()
+ disks_module = DisksModule(
+ connection=connection,
+ module=module,
+ service=disks_service,
+ )
+
+ lun = module.params.get('logical_unit')
+ if lun:
+ disk = _search_by_lun(disks_service, lun.get('id'))
+
+ ret = None
+ # First take care of creating the VM, if needed:
+ if state == 'present' or state == 'detached' or state == 'attached':
+ ret = disks_module.create(
+ entity=disk,
+ result_state=otypes.DiskStatus.OK if lun is None else None,
+ )
+ # We need to pass ID to the module, so in case we want detach/attach disk
+ # we have this ID specified to attach/detach method:
+ module.params['id'] = ret['id'] if disk is None else disk.id
+ elif state == 'absent':
+ ret = disks_module.remove()
+
+ # If VM was passed attach/detach disks to/from the VM:
+ if 'vm_id' in module.params or 'vm_name' in module.params and state != 'absent':
+ vms_service = connection.system_service().vms_service()
+
+ # If `vm_id` isn't specified, find VM by name:
+ vm_id = module.params['vm_id']
+ if vm_id is None:
+ vm_id = getattr(search_by_name(vms_service, module.params['vm_name']), 'id', None)
+
+ if vm_id is None:
+ module.fail_json(
+ msg="VM don't exists, please create it first."
+ )
+
+ disk_attachments_service = vms_service.vm_service(vm_id).disk_attachments_service()
+ disk_attachments_module = DiskAttachmentsModule(
+ connection=connection,
+ module=module,
+ service=disk_attachments_service,
+ changed=ret['changed'] if ret else False,
+ )
+
+ if state == 'present' or state == 'attached':
+ ret = disk_attachments_module.create()
+ elif state == 'detached':
+ ret = disk_attachments_module.remove()
+
+ module.exit_json(**ret)
+ except Exception as e:
+ module.fail_json(msg=str(e))
+ finally:
+ connection.close(logout=False)
+
+
+from ansible.module_utils.basic import *
+if __name__ == "__main__":
+ main()
diff --git a/cloud/ovirt/ovirt_vms.py b/cloud/ovirt/ovirt_vms.py
new file mode 100644
index 00000000..8aa4a983
--- /dev/null
+++ b/cloud/ovirt/ovirt_vms.py
@@ -0,0 +1,804 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016 Red Hat, Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+try:
+ import ovirtsdk4 as sdk
+ import ovirtsdk4.types as otypes
+except ImportError:
+ pass
+
+from ansible.module_utils.ovirt import *
+
+
+DOCUMENTATION = '''
+---
+module: ovirt_vms
+short_description: "Module to manage Virtual Machines in oVirt."
+version_added: "2.2"
+author: "Ondra Machacek (@machacekondra)"
+description:
+ - "This module manages whole lifecycle of the Virtual Machine(VM) in oVirt. Since VM can hold many states in oVirt,
+ this see notes to see how the states of the VM are handled."
+options:
+ name:
+ description:
+ - "Name of the the Virtual Machine to manage. If VM don't exists C(name) is required.
+ Otherwise C(id) or C(name) can be used."
+ id:
+ description:
+ - "ID of the the Virtual Machine to manage."
+ state:
+ description:
+ - "Should the Virtual Machine be running/stopped/present/absent/suspended/next_run."
+ - "I(present) and I(running) are equal states."
+ - "I(next_run) state updates the VM and if the VM has next run configuration it will be rebooted."
+ - "Please check I(notes) to more detailed description of states."
+ choices: ['running', 'stopped', 'present', 'absent', 'suspended', 'next_run']
+ default: present
+ cluster:
+ description:
+ - "Name of the cluster, where Virtual Machine should be created. Required if creating VM."
+ template:
+ description:
+ - "Name of the template, which should be used to create Virtual Machine. Required if creating VM."
+ - "If template is not specified and VM doesn't exist, VM will be created from I(Blank) template."
+ memory:
+ description:
+ - "Amount of memory of the Virtual Machine. Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB)."
+ - "Default value is set by engine."
+ memory_guaranteed:
+ description:
+ - "Amount of minimal guaranteed memory of the Virtual Machine.
+ Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB)."
+ - "C(memory_guaranteed) parameter can't be lower than C(memory) parameter. Default value is set by engine."
+ cpu_shares:
+ description:
+ - "Set a CPU shares for this Virtual Machine. Default value is set by oVirt engine."
+ cpu_cores:
+ description:
+ - "Number of virtual CPUs cores of the Virtual Machine. Default value is set by oVirt engine."
+ cpu_sockets:
+ description:
+ - "Number of virtual CPUs sockets of the Virtual Machine. Default value is set by oVirt engine."
+ type:
+ description:
+ - "Type of the Virtual Machine. Default value is set by oVirt engine."
+ choices: [server, desktop]
+ operating_system:
+ description:
+ - "Operating system of the Virtual Machine. Default value is set by oVirt engine."
+ choices: [
+ rhel_6_ppc64, other, freebsd, windows_2003x64, windows_10, rhel_6x64, rhel_4x64, windows_2008x64,
+ windows_2008R2x64, debian_7, windows_2012x64, ubuntu_14_04, ubuntu_12_04, ubuntu_13_10, windows_8x64,
+ other_linux_ppc64, windows_2003, other_linux, windows_10x64, windows_2008, rhel_3, rhel_5, rhel_4,
+ other_ppc64, sles_11, rhel_6, windows_xp, rhel_7x64, freebsdx64, rhel_7_ppc64, windows_7, rhel_5x64,
+ ubuntu_14_04_ppc64, sles_11_ppc64, windows_8, windows_2012R2x64, windows_2008r2x64, ubuntu_13_04,
+ ubuntu_12_10, windows_7x64
+ ]
+ boot_devices:
+ description:
+ - "List of boot devices which should be used to boot. Choices I(network), I(hd) and I(cdrom)."
+ - "For example: ['cdrom', 'hd']. Default value is set by oVirt engine."
+ host:
+ description:
+ - "Specify host where Virtual Machine should be running. By default the host is chosen by engine scheduler."
+ - "This parameter is used only when C(state) is I(running) or I(present)."
+ high_availability:
+ description:
+ - "If I(True) Virtual Machine will be set as highly available."
+ - "If I(False) Virtual Machine won't be set as highly available."
+ - "If no value is passed, default value is set by oVirt engine."
+ delete_protected:
+ description:
+ - "If I(True) Virtual Machine will be set as delete protected."
+ - "If I(False) Virtual Machine won't be set as delete protected."
+ - "If no value is passed, default value is set by oVirt engine."
+ stateless:
+ description:
+ - "If I(True) Virtual Machine will be set as stateless."
+ - "If I(False) Virtual Machine will be unset as stateless."
+ - "If no value is passed, default value is set by oVirt engine."
+ clone:
+ description:
+ - "If I(True) then the disks of the created virtual machine will be cloned and independent of the template."
+ - "This parameter is used only when C(state) is I(running) or I(present) and VM didn't exist before."
+ default: False
+ clone_permissions:
+ description:
+ - "If I(True) then the permissions of the template (only the direct ones, not the inherited ones)
+ will be copied to the created virtual machine."
+ - "This parameter is used only when C(state) is I(running) or I(present) and VM didn't exist before."
+ default: False
+ cd_iso:
+ description:
+ - "ISO file from ISO storage domain which should be attached to Virtual Machine."
+ - "If you pass empty string the CD will be ejected from VM."
+ - "If used with C(state) I(running) or I(present) and VM is running the CD will be attached to VM."
+ - "If used with C(state) I(running) or I(present) and VM is down the CD will be attached to VM persistently."
+ force:
+ description:
+ - "Please check to I(Synopsis) to more detailed description of force parameter, it can behave differently
+ in different situations."
+ default: False
+ nics:
+ description:
+ - "List of NICs, which should be attached to Virtual Machine. NIC is described by following dictionary:"
+ - "C(name) - Name of the NIC."
+ - "C(profile_name) - Profile name where NIC should be attached."
+ - "C(interface) - Type of the network interface. One of following: I(virtio), I(e1000), I(rtl8139), default is I(virtio)."
+ - "C(mac_address) - Custom MAC address of the network interface, by default it's obtained from MAC pool."
+ - "C(Note:)"
+ - "This parameter is used only when C(state) is I(running) or I(present) and is able to only create NICs.
+ To manage NICs of the VM in more depth please use M(ovirt_nics) module instead."
+ disks:
+ description:
+ - "List of disks, which should be attached to Virtual Machine. Disk is described by following dictionary:"
+ - "C(name) - Name of the disk. Either C(name) or C(id) is reuqired."
+ - "C(id) - ID of the disk. Either C(name) or C(id) is reuqired."
+ - "C(interface) - Interface of the disk, either I(virtio) or I(IDE), default is I(virtio)."
+ - "C(bootable) - I(True) if the disk should be bootable, default is non bootable."
+ - "C(activate) - I(True) if the disk should be activated, default is activated."
+ - "C(Note:)"
+ - "This parameter is used only when C(state) is I(running) or I(present) and is able to only attach disks.
+ To manage disks of the VM in more depth please use M(ovirt_disks) module instead."
+ sysprep:
+ description:
+ - "Dictionary with values for Windows Virtual Machine initialization using sysprep:"
+ - "C(host_name) - Hostname to be set to Virtual Machine when deployed."
+ - "C(active_directory_ou) - Active Directory Organizational Unit, to be used for login of user."
+ - "C(org_name) - Organization name to be set to Windows Virtual Machine."
+ - "C(domain) - Domain to be set to Windows Virtual Machine."
+ - "C(timezone) - Timezone to be set to Windows Virtual Machine."
+ - "C(ui_language) - UI language of the Windows Virtual Machine."
+ - "C(system_locale) - System localization of the Windows Virtual Machine."
+ - "C(input_locale) - Input localization of the Windows Virtual Machine."
+ - "C(windows_license_key) - License key to be set to Windows Virtual Machine."
+ - "C(user_name) - Username to be used for set password to Windows Virtual Machine."
+ - "C(root_password) - Password to be set for username to Windows Virtual Machine."
+ cloud_init:
+ description:
+ - "Dictionary with values for Unix-like Virtual Machine initialization using cloud init:"
+ - "C(host_name) - Hostname to be set to Virtual Machine when deployed."
+ - "C(timezone) - Timezone to be set to Virtual Machine when deployed."
+ - "C(user_name) - Username to be used to set password to Virtual Machine when deployed."
+ - "C(root_password) - Password to be set for user specified by C(user_name) parameter."
+ - "C(authorized_ssh_keys) - Use this SSH keys to login to Virtual Machine."
+ - "C(regenerate_ssh_keys) - If I(True) SSH keys will be regenerated on Virtual Machine."
+ - "C(custom_script) - Cloud-init script which will be executed on Virtual Machine when deployed."
+ - "C(dns_servers) - DNS servers to be configured on Virtual Machine."
+ - "C(dns_search) - DNS search domains to be configured on Virtual Machine."
+ - "C(nic_boot_protocol) - Set boot protocol of the network interface of Virtual Machine. Can be one of None, DHCP or Static."
+ - "C(nic_ip_address) - If boot protocol is static, set this IP address to network interface of Virtual Machine."
+ - "C(nic_netmask) - If boot protocol is static, set this netmask to network interface of Virtual Machine."
+ - "C(nic_gateway) - If boot protocol is static, set this gateway to network interface of Virtual Machine."
+ - "C(nic_name) - Set name to network interface of Virtual Machine."
+ - "C(nic_on_boot) - If I(True) network interface will be set to start on boot."
+notes:
+ - "If VM is in I(UNASSIGNED) or I(UNKNOWN) state before any operation, the module will fail.
+ If VM is in I(IMAGE_LOCKED) state before any operation, we try to wait for VM to be I(DOWN).
+ If VM is in I(SAVING_STATE) state before any operation, we try to wait for VM to be I(SUSPENDED).
+ If VM is in I(POWERING_DOWN) state before any operation, we try to wait for VM to be I(UP) or I(DOWN). VM can
+ get into I(UP) state from I(POWERING_DOWN) state, when there is no ACPI or guest agent running inside VM, or
+ if the shutdown operation fails.
+ When user specify I(UP) C(state), we always wait to VM to be in I(UP) state in case VM is I(MIGRATING),
+ I(REBOOTING), I(POWERING_UP), I(RESTORING_STATE), I(WAIT_FOR_LAUNCH). In other states we run start operation on VM.
+ When user specify I(stopped) C(state), and If user pass C(force) parameter set to I(true) we forcibly stop the VM in
+ any state. If user don't pass C(force) parameter, we always wait to VM to be in UP state in case VM is
+ I(MIGRATING), I(REBOOTING), I(POWERING_UP), I(RESTORING_STATE), I(WAIT_FOR_LAUNCH). If VM is in I(PAUSED) or
+ I(SUSPENDED) state, we start the VM. Then we gracefully shutdown the VM.
+ When user specify I(suspended) C(state), we always wait to VM to be in UP state in case VM is I(MIGRATING),
+ I(REBOOTING), I(POWERING_UP), I(RESTORING_STATE), I(WAIT_FOR_LAUNCH). If VM is in I(PAUSED) or I(DOWN) state,
+ we start the VM. Then we suspend the VM.
+ When user specify I(absent) C(state), we forcibly stop the VM in any state and remove it."
+extends_documentation_fragment: ovirt
+'''
+
+EXAMPLES = '''
+# Examples don't contain auth parameter for simplicity,
+# look at ovirt_auth module to see how to reuse authentication:
+
+# Creates a new Virtual Machine from template named 'rhel7_template'
+ovirt_vms:
+ state: present
+ name: myvm
+ template: rhel7_template
+
+# Creates a new server rhel7 Virtual Machine from Blank template
+# on brq01 cluster with 2GiB memory and 2 vcpu cores/sockets
+# and attach bootable disk with name rhel7_disk and attach virtio NIC
+ovirt_vms:
+ state: present
+ cluster: brq01
+ name: myvm
+ memory: 2GiB
+ cpu_cores: 2
+ cpu_sockets: 2
+ cpu_shares: 1024
+ type: server
+ operating_system: rhel_7x64
+ disks:
+ - name: rhel7_disk
+ bootable: True
+ nics:
+ - name: nic1
+
+# Run VM with cloud init:
+ovirt_vms:
+ name: rhel7
+ template: rhel7
+ cluster: Default
+ memory: 1GiB
+ high_availability: true
+ cloud_init:
+ nic_boot_protocol: static
+ nic_ip_address: 10.34.60.86
+ nic_netmask: 255.255.252.0
+ nic_gateway: 10.34.63.254
+ nic_name: eth1
+ nic_on_boot: true
+ host_name: example.com
+ custom_script: |
+ write_files:
+ - content: |
+ Hello, world!
+ path: /tmp/greeting.txt
+ permissions: '0644'
+ user_name: root
+ root_password: super_password
+
+# Run VM with sysprep:
+ovirt_vms:
+ name: windows2012R2_AD
+ template: windows2012R2
+ cluster: Default
+ memory: 3GiB
+ high_availability: true
+ sysprep:
+ host_name: windowsad.example.com
+ user_name: Administrator
+ root_password: SuperPassword123
+
+# Migrate/Run VM to/on host named 'host1'
+ovirt_vms:
+ state: running
+ name: myvm
+ host: host1
+
+# Change Vm's CD:
+ovirt_vms:
+ name: myvm
+ cd_iso: drivers.iso
+
+# Eject Vm's CD:
+ovirt_vms:
+ name: myvm
+ cd_iso: ''
+
+# Boot VM from CD:
+ovirt_vms:
+ name: myvm
+ cd_iso: centos7_x64.iso
+ boot_devices:
+ - cdrom
+
+# Stop vm:
+ovirt_vms:
+ state: stopped
+ name: myvm
+
+# Upgrade memory to already created VM:
+ovirt_vms:
+ name: myvm
+ memory: 4GiB
+
+# Hot plug memory to already created and running VM:
+# (VM won't be restarted)
+ovirt_vms:
+ name: myvm
+ memory: 4GiB
+
+# When change on the VM needs restart of the VM, use next_run state,
+# The VM will be updated and rebooted if there are any changes.
+# If present state would be used, VM won't be restarted.
+ovirt_vms:
+ state: next_run
+ name: myvm
+ boot_devices:
+ - network
+
+# Remove VM, if VM is running it will be stopped:
+ovirt_vms:
+ state: absent
+ name: myvm
+'''
+
+
+RETURN = '''
+id:
+ description: ID of the VM which is managed
+ returned: On success if VM is found.
+ type: str
+ sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
+vm:
+ description: "Dictionary of all the VM attributes. VM attributes can be found on your oVirt instance
+ at following url: https://ovirt.example.com/ovirt-engine/api/model#types/vm."
+ returned: On success if VM is found.
+'''
+
+
+class VmsModule(BaseModule):
+
+ def build_entity(self):
+ return otypes.Vm(
+ name=self._module.params['name'],
+ cluster=otypes.Cluster(
+ name=self._module.params['cluster']
+ ) if self._module.params['cluster'] else None,
+ template=otypes.Template(
+ name=self._module.params['template']
+ ) if self._module.params['template'] else None,
+ stateless=self._module.params['stateless'],
+ delete_protected=self._module.params['delete_protected'],
+ high_availability=otypes.HighAvailability(
+ enabled=self._module.params['high_availability']
+ ) if self._module.params['high_availability'] is not None else None,
+ cpu=otypes.Cpu(
+ topology=otypes.CpuTopology(
+ cores=self._module.params['cpu_cores'],
+ sockets=self._module.params['cpu_sockets'],
+ )
+ ) if (
+ self._module.params['cpu_cores'] or self._module.params['cpu_sockets']
+ ) else None,
+ cpu_shares=self._module.params['cpu_shares'],
+ os=otypes.OperatingSystem(
+ type=self._module.params['operating_system'],
+ boot=otypes.Boot(
+ devices=[
+ otypes.BootDevice(dev) for dev in self._module.params['boot_devices']
+ ],
+ ) if self._module.params['boot_devices'] else None,
+ ) if (
+ self._module.params['operating_system'] or self._module.params['boot_devices']
+ ) else None,
+ type=otypes.VmType(
+ self._module.params['type']
+ ) if self._module.params['type'] else None,
+ memory=convert_to_bytes(
+ self._module.params['memory']
+ ) if self._module.params['memory'] else None,
+ memory_policy=otypes.MemoryPolicy(
+ guaranteed=convert_to_bytes(self._module.params['memory_guaranteed']),
+ ) if self._module.params['memory_guaranteed'] else None,
+ )
+
+ def update_check(self, entity):
+ return (
+ equal(self._module.params.get('cluster'), get_link_name(self._connection, entity.cluster)) and
+ equal(convert_to_bytes(self._module.params['memory']), entity.memory) and
+ equal(convert_to_bytes(self._module.params['memory_guaranteed']), entity.memory_policy.guaranteed) and
+ equal(self._module.params.get('cpu_cores'), entity.cpu.topology.cores) and
+ equal(self._module.params.get('cpu_sockets'), entity.cpu.topology.sockets) and
+ equal(self._module.params.get('type'), str(entity.type)) and
+ equal(self._module.params.get('operating_system'), str(entity.os.type)) and
+ equal(self._module.params.get('high_availability'), entity.high_availability.enabled) and
+ equal(self._module.params.get('stateless'), entity.stateless) and
+ equal(self._module.params.get('cpu_shares'), entity.cpu_shares) and
+ equal(self._module.params.get('delete_protected'), entity.delete_protected) and
+ equal(self._module.params.get('boot_devices'), [str(dev) for dev in getattr(entity.os, 'devices', [])])
+ )
+
+ def pre_create(self, entity):
+ # If VM don't exists, and template is not specified, set it to Blank:
+ if entity is None:
+ if self._module.params.get('template') is None:
+ self._module.params['template'] = 'Blank'
+
+ def post_update(self, entity):
+ self.post_create(entity)
+
+ def post_create(self, entity):
+ # After creation of the VM, attach disks and NICs:
+ self.changed = self.__attach_disks(entity)
+ self.changed = self.__attach_nics(entity)
+
+ def pre_remove(self, entity):
+ # Forcibly stop the VM, if it's not in DOWN state:
+ if entity.status != otypes.VmStatus.DOWN:
+ if not self._module.check_mode:
+ self.changed = self.action(
+ action='stop',
+ action_condition=lambda vm: vm.status != otypes.VmStatus.DOWN,
+ wait_condition=lambda vm: vm.status == otypes.VmStatus.DOWN,
+ )['changed']
+
+ def __suspend_shutdown_common(self, vm_service):
+ if vm_service.get().status in [
+ otypes.VmStatus.MIGRATING,
+ otypes.VmStatus.POWERING_UP,
+ otypes.VmStatus.REBOOT_IN_PROGRESS,
+ otypes.VmStatus.WAIT_FOR_LAUNCH,
+ otypes.VmStatus.UP,
+ otypes.VmStatus.RESTORING_STATE,
+ ]:
+ self._wait_for_UP(vm_service)
+
+ def _pre_shutdown_action(self, entity):
+ vm_service = self._service.vm_service(entity.id)
+ self.__suspend_shutdown_common(vm_service)
+ if entity.status in [otypes.VmStatus.SUSPENDED, otypes.VmStatus.PAUSED]:
+ vm_service.start()
+ self._wait_for_UP(vm_service)
+ return vm_service.get()
+
+ def _pre_suspend_action(self, entity):
+ vm_service = self._service.vm_service(entity.id)
+ self.__suspend_shutdown_common(vm_service)
+ if entity.status in [otypes.VmStatus.PAUSED, otypes.VmStatus.DOWN]:
+ vm_service.start()
+ self._wait_for_UP(vm_service)
+ return vm_service.get()
+
+ def _post_start_action(self, entity):
+ vm_service = self._service.service(entity.id)
+ self._wait_for_UP(vm_service)
+ self._attach_cd(vm_service.get())
+ self._migrate_vm(vm_service.get())
+
+ def _attach_cd(self, entity):
+ cd_iso = self._module.params['cd_iso']
+ if cd_iso is not None:
+ vm_service = self._service.service(entity.id)
+ current = vm_service.get().status == otypes.VmStatus.UP
+ cdroms_service = vm_service.cdroms_service()
+ cdrom_device = cdroms_service.list()[0]
+ cdrom_service = cdroms_service.cdrom_service(cdrom_device.id)
+ cdrom = cdrom_service.get(current=current)
+ if getattr(cdrom.file, 'id', '') != cd_iso:
+ if not self._module.check_mode:
+ cdrom_service.update(
+ cdrom=otypes.Cdrom(
+ file=otypes.File(id=cd_iso)
+ ),
+ current=current,
+ )
+ self.changed = True
+
+ return entity
+
+ def _migrate_vm(self, entity):
+ vm_host = self._module.params['host']
+ vm_service = self._service.vm_service(entity.id)
+ if vm_host is not None:
+ # In case VM is preparing to be UP, wait to be up, to migrate it:
+ if entity.status == otypes.VmStatus.UP:
+ hosts_service = self._connection.system_service().hosts_service()
+ current_vm_host = hosts_service.host_service(entity.host.id).get().name
+ if vm_host != current_vm_host:
+ if not self._module.check_mode:
+ vm_service.migrate(host=otypes.Host(name=vm_host))
+ self._wait_for_UP(vm_service)
+ self.changed = True
+
+ return entity
+
+ def _wait_for_UP(self, vm_service):
+ wait(
+ service=vm_service,
+ condition=lambda vm: vm.status == otypes.VmStatus.UP,
+ wait=self._module.params['wait'],
+ timeout=self._module.params['timeout'],
+ )
+
+ def __attach_disks(self, entity):
+ disks_service = self._connection.system_service().disks_service()
+
+ for disk in self._module.params['disks']:
+ # If disk ID is not specified, find disk by name:
+ disk_id = disk.get('id')
+ if disk_id is None:
+ disk_id = getattr(
+ search_by_name(
+ service=disks_service,
+ name=disk.get('name')
+ ),
+ 'id',
+ None
+ )
+
+ # Attach disk to VM:
+ disk_attachments_service = self._service.service(entity.id).disk_attachments_service()
+ if disk_attachments_service.attachment_service(disk_id).get() is None:
+ if not self._module.check_mode:
+ disk_attachments_service.add(
+ otypes.DiskAttachment(
+ disk=otypes.Disk(
+ id=disk_id,
+ active=disk.get('activate', True),
+ ),
+ interface=otypes.DiskInterface(
+ disk.get('interface', 'virtio')
+ ),
+ bootable=disk.get('bootable', False),
+ )
+ )
+ self.changed = True
+
+ def __attach_nics(self, entity):
+ # Attach NICs to VM, if specified:
+ vnic_profiles_service = self._connection.system_service().vnic_profiles_service()
+ nics_service = self._service.service(entity.id).nics_service()
+ for nic in self._module.params['nics']:
+ if search_by_name(nics_service, nic.get('name')) is None:
+ if not self._module.check_mode:
+ nics_service.add(
+ otypes.Nic(
+ name=nic.get('name'),
+ interface=otypes.NicInterface(
+ nic.get('interface', 'virtio')
+ ),
+ vnic_profile=otypes.VnicProfile(
+ id=search_by_name(
+ vnic_profiles_service,
+ nic.get('profile_name'),
+ ).id
+ ) if nic.get('profile_name') else None,
+ mac=otypes.Mac(
+ address=nic.get('mac_address')
+ ) if nic.get('mac_address') else None,
+ )
+ )
+ self.changed = True
+
+
+def _get_initialization(sysprep, cloud_init):
+ initialization = None
+ if cloud_init:
+ initialization = otypes.Initialization(
+ nic_configurations=[
+ otypes.NicConfiguration(
+ boot_protocol=otypes.BootProtocol(
+ cloud_init.pop('nic_boot_protocol').lower()
+ ) if cloud_init.get('nic_boot_protocol') else None,
+ name=cloud_init.pop('nic_name'),
+ on_boot=cloud_init.pop('nic_on_boot'),
+ ip=otypes.Ip(
+ address=cloud_init.pop('nic_ip_address'),
+ netmask=cloud_init.pop('nic_netmask'),
+ gateway=cloud_init.pop('nic_gateway'),
+ ) if (
+ cloud_init.get('nic_gateway') is not None or
+ cloud_init.get('nic_netmask') is not None or
+ cloud_init.get('nic_ip_address') is not None
+ ) else None,
+ )
+ ] if (
+ cloud_init.get('nic_gateway') is not None or
+ cloud_init.get('nic_netmask') is not None or
+ cloud_init.get('nic_ip_address') is not None or
+ cloud_init.get('nic_boot_protocol') is not None or
+ cloud_init.get('nic_on_boot') is not None
+ ) else None,
+ **cloud_init
+ )
+ elif sysprep:
+ initialization = otypes.Initialization(
+ **sysprep
+ )
+ return initialization
+
+
+def control_state(vm, vms_service, module):
+ if vm is None:
+ return
+
+ force = module.params['force']
+ state = module.params['state']
+
+ vm_service = vms_service.vm_service(vm.id)
+ if vm.status == otypes.VmStatus.IMAGE_LOCKED:
+ wait(
+ service=vm_service,
+ condition=lambda vm: vm.status == otypes.VmStatus.DOWN,
+ )
+ elif vm.status == otypes.VmStatus.SAVING_STATE:
+ # Result state is SUSPENDED, we should wait to be suspended:
+ wait(
+ service=vm_service,
+ condition=lambda vm: vm.status == otypes.VmStatus.SUSPENDED,
+ )
+ elif (
+ vm.status == otypes.VmStatus.UNASSIGNED or
+ vm.status == otypes.VmStatus.UNKNOWN
+ ):
+ # Invalid states:
+ module.fail_json("Not possible to control VM, if it's in '{}' status".format(vm.status))
+ elif vm.status == otypes.VmStatus.POWERING_DOWN:
+ if (force and state == 'stopped') or state == 'absent':
+ vm_service.stop()
+ wait(
+ service=vm_service,
+ condition=lambda vm: vm.status == otypes.VmStatus.DOWN,
+ )
+ else:
+ # If VM is powering down, wait to be DOWN or UP.
+ # VM can end in UP state in case there is no GA
+ # or ACPI on the VM or shutdown operation crashed:
+ wait(
+ service=vm_service,
+ condition=lambda vm: vm.status in [otypes.VmStatus.DOWN, otypes.VmStatus.UP],
+ )
+
+
+def main():
+ argument_spec = ovirt_full_argument_spec(
+ state=dict(
+ choices=['running', 'stopped', 'present', 'absent', 'suspended', 'next_run'],
+ default='present',
+ ),
+ name=dict(default=None),
+ id=dict(default=None),
+ cluster=dict(default=None),
+ template=dict(default=None),
+ disks=dict(default=[], type='list'),
+ memory=dict(default=None),
+ memory_guaranteed=dict(default=None),
+ cpu_sockets=dict(default=None, type='int'),
+ cpu_cores=dict(default=None, type='int'),
+ cpu_shares=dict(default=None, type='int'),
+ type=dict(choices=['server', 'desktop']),
+ operating_system=dict(
+ default=None,
+ choices=[
+ 'rhel_6_ppc64', 'other', 'freebsd', 'windows_2003x64', 'windows_10',
+ 'rhel_6x64', 'rhel_4x64', 'windows_2008x64', 'windows_2008R2x64',
+ 'debian_7', 'windows_2012x64', 'ubuntu_14_04', 'ubuntu_12_04',
+ 'ubuntu_13_10', 'windows_8x64', 'other_linux_ppc64', 'windows_2003',
+ 'other_linux', 'windows_10x64', 'windows_2008', 'rhel_3', 'rhel_5',
+ 'rhel_4', 'other_ppc64', 'sles_11', 'rhel_6', 'windows_xp', 'rhel_7x64',
+ 'freebsdx64', 'rhel_7_ppc64', 'windows_7', 'rhel_5x64',
+ 'ubuntu_14_04_ppc64', 'sles_11_ppc64', 'windows_8',
+ 'windows_2012R2x64', 'windows_2008r2x64', 'ubuntu_13_04',
+ 'ubuntu_12_10', 'windows_7x64',
+ ],
+ ),
+ cd_iso=dict(default=None),
+ boot_devices=dict(default=None, type='list'),
+ high_availability=dict(type='bool'),
+ stateless=dict(type='bool'),
+ delete_protected=dict(type='bool'),
+ force=dict(type='bool', default=False),
+ nics=dict(default=[], type='list'),
+ cloud_init=dict(type='dict'),
+ sysprep=dict(type='dict'),
+ host=dict(default=None),
+ clone=dict(type='bool', default=False),
+ clone_permissions=dict(type='bool', default=False),
+ )
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+ check_sdk(module)
+ check_params(module)
+
+ try:
+ state = module.params['state']
+ connection = create_connection(module.params.pop('auth'))
+ vms_service = connection.system_service().vms_service()
+ vms_module = VmsModule(
+ connection=connection,
+ module=module,
+ service=vms_service,
+ )
+ vm = vms_module.search_entity()
+
+ control_state(vm, vms_service, module)
+ if state == 'present' or state == 'running' or state == 'next_run':
+ cloud_init = module.params['cloud_init']
+ sysprep = module.params['sysprep']
+
+ # In case VM don't exist, wait for VM DOWN state,
+ # otherwise don't wait for any state, just update VM:
+ vms_module.create(
+ entity=vm,
+ result_state=otypes.VmStatus.DOWN if vm is None else None,
+ clone=module.params['clone'],
+ clone_permissions=module.params['clone_permissions'],
+ )
+ ret = vms_module.action(
+ action='start',
+ post_action=vms_module._post_start_action,
+ action_condition=lambda vm: (
+ vm.status not in [
+ otypes.VmStatus.MIGRATING,
+ otypes.VmStatus.POWERING_UP,
+ otypes.VmStatus.REBOOT_IN_PROGRESS,
+ otypes.VmStatus.WAIT_FOR_LAUNCH,
+ otypes.VmStatus.UP,
+ otypes.VmStatus.RESTORING_STATE,
+ ]
+ ),
+ wait_condition=lambda vm: vm.status == otypes.VmStatus.UP,
+ # Start action kwargs:
+ use_cloud_init=cloud_init is not None,
+ use_sysprep=sysprep is not None,
+ vm=otypes.Vm(
+ placement_policy=otypes.VmPlacementPolicy(
+ hosts=[otypes.Host(name=module.params['host'])]
+ ) if module.params['host'] else None,
+ initialization=_get_initialization(sysprep, cloud_init),
+ ),
+ )
+
+ if state == 'next_run':
+ # Apply next run configuration, if needed:
+ vm = vms_service.vm_service(ret['id']).get()
+ if vm.next_run_configuration_exists:
+ ret = vms_module.action(
+ action='reboot',
+ entity=vm,
+ action_condition=lambda vm: vm.status == otypes.VmStatus.UP,
+ wait_condition=lambda vm: vm.status == otypes.VmStatus.UP,
+ )
+ elif state == 'stopped':
+ vms_module.create(
+ clone=module.params['clone'],
+ clone_permissions=module.params['clone_permissions'],
+ )
+ if module.params['force']:
+ ret = vms_module.action(
+ action='stop',
+ post_action=vms_module._attach_cd,
+ action_condition=lambda vm: vm.status != otypes.VmStatus.DOWN,
+ wait_condition=lambda vm: vm.status == otypes.VmStatus.DOWN,
+ )
+ else:
+ ret = vms_module.action(
+ action='shutdown',
+ pre_action=vms_module._pre_shutdown_action,
+ post_action=vms_module._attach_cd,
+ action_condition=lambda vm: vm.status != otypes.VmStatus.DOWN,
+ wait_condition=lambda vm: vm.status == otypes.VmStatus.DOWN,
+ )
+ elif state == 'suspended':
+ vms_module.create(
+ clone=module.params['clone'],
+ clone_permissions=module.params['clone_permissions'],
+ )
+ ret = vms_module.action(
+ action='suspend',
+ pre_action=vms_module._pre_suspend_action,
+ action_condition=lambda vm: vm.status != otypes.VmStatus.SUSPENDED,
+ wait_condition=lambda vm: vm.status == otypes.VmStatus.SUSPENDED,
+ )
+ elif state == 'absent':
+ ret = vms_module.remove()
+
+ module.exit_json(**ret)
+ except Exception as e:
+ module.fail_json(msg=str(e))
+ finally:
+ connection.close(logout=False)
+
+from ansible.module_utils.basic import *
+if __name__ == "__main__":
+ main()