From 2a885a28d74a310ec6cdc77a53a94ffc0a30b9b1 Mon Sep 17 00:00:00 2001 From: mnecas Date: Sun, 10 Nov 2019 19:02:35 +0100 Subject: [PATCH 1/4] bump ansible to 2.9 --- README.md | 4 +- automation.yaml | 6 +- build.sh | 2 +- library/README.md | 28 - library/ovirt_event_28.py | 249 --- library/ovirt_host_28.py | 637 ------- library/ovirt_job_29.py | 235 --- library/ovirt_vm_28.py | 2507 ------------------------- meta/main.yml | 2 +- ovirt-ansible-cluster-upgrade.spec.in | 2 +- tasks/main.yml | 14 +- tasks/upgrade.yml | 8 +- tests/requirements.txt | 2 +- 13 files changed, 20 insertions(+), 3676 deletions(-) delete mode 100644 library/README.md delete mode 100644 library/ovirt_event_28.py delete mode 100644 library/ovirt_host_28.py delete mode 100644 library/ovirt_job_29.py delete mode 100644 library/ovirt_vm_28.py diff --git a/README.md b/README.md index e99c06f..24c766e 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ So you can use any of this name. This documentation and examples in this reposit Requirements ------------ - * Ansible version 2.5 or higher - * Python SDK version 4.2 or higher + * Ansible version 2.9 or higher + * Python SDK version 4.3 or higher Role Variables -------------- diff --git a/automation.yaml b/automation.yaml index 28d9c71..fcc3f1f 100644 --- a/automation.yaml +++ b/automation.yaml @@ -1,6 +1,6 @@ distros: - - fc29 - - fc28 + - fc30 - el7 + - el8 release_branches: - master: [ "ovirt-master", "ovirt-4.3", "ovirt-4.2" ] + master: [ "ovirt-master" ] diff --git a/build.sh b/build.sh index 80bffef..a8536ce 100755 --- a/build.sh +++ b/build.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION="1.1.14" +VERSION="1.2.0" MILESTONE=master RPM_RELEASE="0.1.$MILESTONE.$(date -u +%Y%m%d%H%M%S)" diff --git a/library/README.md b/library/README.md deleted file mode 100644 index e22db48..0000000 --- a/library/README.md +++ /dev/null @@ -1,28 +0,0 @@ -Updated modules -=============== - -Here is list of updated modules: - -1. ovirt_hosts.py - -- Check if migration failed during upgrade: [PR](https://github.com/ansible/ansible/pull/46104) - The fix will be available in Ansible 2.8. - -- Improve cheecking of failed migration: [PR](https://github.com/ansible/ansible/pull/50489). - The fix will be available in Ansible 2.8. - -2. ovirt_event.py - -- Add ovirt_event module to manage events in oVirt: [PR](https://github.com/ansible/ansible/pull/52982). - The fix will be available in Ansible 2.8. - -3. ovirt_vm.py - -- Add ovirt_vm module to migrate user migratable vms in oVirt: [PR](https://github.com/oVirt/ovirt-ansible-cluster-upgrade/pull/51). - The fix will be available in Ansible 2.8. - - -4. ovirt_job.py - -- Add ovirt_job module to manage jobs in oVirt: [PR](https://github.com/oVirt/ovirt-ansible-cluster-upgrade/pull/53). - The fix will be available in Ansible 2.9. diff --git a/library/ovirt_event_28.py b/library/ovirt_event_28.py deleted file mode 100644 index ea158e0..0000000 --- a/library/ovirt_event_28.py +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright: (c) 2019, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ovirt_event -short_description: Create or delete an event in oVirt/RHV -author: "Chris Keller (@nasx)" -version_added: "2.8" -description: - - "This module can be used to create or delete an event in oVirt/RHV." -options: - state: - description: - - "Should the event be present/absent." - - "The C(wait) option must be set to false when state is absent." - choices: ['present', 'absent'] - type: str - default: present - - description: - description: - - "Message for the event." - - "Required when state is present." - type: str - - severity: - description: - - "Severity of the event." - - "Required when state is present." - choices: ['error', 'normal', 'warning'] - default: normal - type: str - - origin: - description: - - "Originator of the event." - - "Required when state is present." - type: str - - custom_id: - description: - - "Custom ID for the event. This ID must be unique for each event." - - "Required when state is present." - type: int - - id: - description: - - "The event ID in the oVirt/RHV audit_log table. This ID is not the same as custom_id and is only used when state is absent." - - "Required when state is absent." - type: str - - cluster: - description: - - "The id of the cluster associated with this event." - type: str - - data_center: - description: - - "The id of the data center associated with this event." - type: str - - host: - description: - - "The id of the host associated with this event." - type: str - - storage_domain: - description: - - "The id of the storage domain associated with this event." - type: str - - template: - description: - - "The id of the template associated with this event." - type: str - - user: - description: - - "The id of the user associated with this event." - type: str - - vm: - description: - - "The id of the VM associated with this event." - type: str -extends_documentation_fragment: ovirt -''' - -EXAMPLES = ''' -# Examples don't contain the auth parameter for simplicity, -# look at the ovirt_auth module to see how to reuse authentication. - -- name: Create an event - ovirt_event: - state: present - description: "The file system /home on host xyz is almost full!" - origin: "mymonitor" - custom_id: 123456789 - severity: warning - -- name: Create an event and link it to a specific object - ovirt_event: - state: present - description: "The file system /home is almost full!" - origin: "mymonitor" - custom_id: 123456789 - severity: warning - vm: "c79db183-46ef-44d1-95f9-1a368c516c19" - -- name: Remove an event - ovirt_event: - state: absent - id: 123456789 - wait: false -''' - -RETURN = ''' -id: - description: "ID of the event that was created." - returned: "On success." - type: str -event: - description: "Dictionary of all the Event attributes. All event attributes can be found at the following url: - http://ovirt.github.io/ovirt-engine-api-model/master/#types/event" - returned: "On success." - type: dict -''' - -import traceback - -try: - import ovirtsdk4.types as otypes -except ImportError: - pass - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ovirt import ( - BaseModule, - check_sdk, - check_params, - create_connection, - equal, - get_dict_of_struct, - ovirt_full_argument_spec, -) - - -class EventsModule(BaseModule): - - def build_entity(self): - return otypes.Event( - description=self._module.params['description'], - severity=otypes.LogSeverity(self._module.params['severity']), - origin=self._module.params['origin'], - custom_id=self._module.params['custom_id'], - id=self._module.params['id'], - cluster=otypes.Cluster( - id=self._module.params['cluster'] - ) if self._module.params['cluster'] is not None else None, - data_center=otypes.DataCenter( - id=self._module.params['data_center'] - ) if self._module.params['data_center'] is not None else None, - host=otypes.Host( - id=self._module.params['host'] - ) if self._module.params['host'] is not None else None, - storage_domain=otypes.StorageDomain( - id=self._module.params['storage_domain'] - ) if self._module.params['storage_domain'] is not None else None, - template=otypes.Template( - id=self._module.params['template'] - ) if self._module.params['template'] is not None else None, - user=otypes.User( - id=self._module.params['user'] - ) if self._module.params['user'] is not None else None, - vm=otypes.Vm( - id=self._module.params['vm'] - ) if self._module.params['vm'] is not None else None, - ) - - -def main(): - argument_spec = ovirt_full_argument_spec( - state=dict( - choices=['present', 'absent'], - default='present', - ), - description=dict(default=None), - severity=dict( - choices=['error', 'normal', 'warning'], - default='normal', - ), - origin=dict(default=None), - custom_id=dict(default=None, type='int'), - id=dict(default=None), - cluster=dict(default=None), - data_center=dict(default=None), - host=dict(default=None), - storage_domain=dict(default=None), - template=dict(default=None), - user=dict(default=None), - vm=dict(default=None), - ) - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - - check_sdk(module) - - # Wait must be set to false if state == absent - - if module.params['state'] == 'absent' and module.params['wait'] is not False: - module.fail_json(msg='When "state" is absent, "wait" must be set to false.') - - try: - auth = module.params.pop('auth') - connection = create_connection(auth) - events_service = connection.system_service().events_service() - events_module = EventsModule( - connection=connection, - module=module, - service=events_service, - ) - - state = module.params['state'] - if state == 'present': - ret = events_module.create() - elif state == 'absent': - ret = events_module.remove() - - module.exit_json(**ret) - except Exception as e: - module.fail_json(msg=str(e), exception=traceback.format_exc()) - finally: - connection.close(logout=auth.get('token') is None) - - -if __name__ == "__main__": - main() diff --git a/library/ovirt_host_28.py b/library/ovirt_host_28.py deleted file mode 100644 index bdbe3a7..0000000 --- a/library/ovirt_host_28.py +++ /dev/null @@ -1,637 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2016 Red Hat, Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ovirt_host -short_description: Module to manage hosts in oVirt/RHV -version_added: "2.3" -author: "Ondra Machacek (@machacekondra)" -description: - - "Module to manage hosts in oVirt/RHV" -options: - id: - description: - - "ID of the host to manage." - version_added: "2.8" - name: - description: - - "Name of the host to manage." - required: true - state: - description: - - "State which should a host to be in after successful completion." - - "I(iscsilogin) and I(iscsidiscover) are supported since version 2.4." - choices: [ - 'present', 'absent', 'maintenance', 'upgraded', 'started', - 'restarted', 'stopped', 'reinstalled', 'iscsidiscover', 'iscsilogin' - ] - default: present - comment: - description: - - "Description of the host." - timeout: - description: - - "The amount of time in seconds the module should wait for the host to - get into desired state." - default: 600 - cluster: - description: - - "Name of the cluster, where host should be created." - address: - description: - - "Host address. It can be either FQDN (preferred) or IP address." - password: - description: - - "Password of the root. It's required in case C(public_key) is set to I(False)." - public_key: - description: - - "I(True) if the public key should be used to authenticate to host." - - "It's required in case C(password) is not set." - default: False - type: bool - aliases: ['ssh_public_key'] - kdump_integration: - description: - - "Specify if host will have enabled Kdump integration." - choices: ['enabled', 'disabled'] - spm_priority: - description: - - "SPM priority of the host. Integer value from 1 to 10, where higher number means higher priority." - override_iptables: - description: - - "If True host iptables will be overridden by host deploy script." - - "Note that C(override_iptables) is I(false) by default in oVirt/RHV." - type: bool - force: - description: - - "If True host will be forcibly moved to desired state." - default: False - type: bool - override_display: - description: - - "Override the display address of all VMs on this host with specified address." - type: bool - kernel_params: - description: - - "List of kernel boot parameters." - - "Following are most common kernel parameters used for host:" - - "Hostdev Passthrough & SR-IOV: intel_iommu=on" - - "Nested Virtualization: kvm-intel.nested=1" - - "Unsafe Interrupts: vfio_iommu_type1.allow_unsafe_interrupts=1" - - "PCI Reallocation: pci=realloc" - - "C(Note:)" - - "Modifying kernel boot parameters settings can lead to a host boot failure. - Please consult the product documentation before doing any changes." - - "Kernel boot parameters changes require host deploy and restart. The host needs - to be I(reinstalled) successfully and then to be I(rebooted) for kernel boot parameters - to be applied." - hosted_engine: - description: - - "If I(deploy) it means this host should deploy also hosted engine - components." - - "If I(undeploy) it means this host should un-deploy hosted engine - components and this host will not function as part of the High - Availability cluster." - choices: - - 'deploy' - - 'undeploy' - power_management_enabled: - description: - - "Enable or disable power management of the host." - - "For more comprehensive setup of PM use C(ovirt_host_pm) module." - version_added: 2.4 - type: bool - activate: - description: - - "If C(state) is I(present) activate the host." - - "This parameter is good to disable, when you don't want to change - the state of host when using I(present) C(state)." - default: True - type: bool - version_added: 2.4 - iscsi: - description: - - "If C(state) is I(iscsidiscover) it means that the iscsi attribute is being - used to discover targets" - - "If C(state) is I(iscsilogin) it means that the iscsi attribute is being - used to login to the specified targets passed as part of the iscsi attribute" - version_added: "2.4" - check_upgrade: - description: - - "If I(true) and C(state) is I(upgraded) run check for upgrade - action before executing upgrade action." - default: True - type: bool - version_added: 2.4 - reboot_after_upgrade: - description: - - "If I(true) and C(state) is I(upgraded) reboot host after successful upgrade." - default: True - type: bool - version_added: 2.6 -extends_documentation_fragment: ovirt -''' - -EXAMPLES = ''' -# Examples don't contain auth parameter for simplicity, -# look at ovirt_auth module to see how to reuse authentication: - -# Add host with username/password supporting SR-IOV. -# Note that override_iptables is false by default in oVirt/RHV: -- ovirt_host: - cluster: Default - name: myhost - address: 10.34.61.145 - password: secret - override_iptables: true - kernel_params: - - intel_iommu=on - -# Add host using public key -- ovirt_host: - public_key: true - cluster: Default - name: myhost2 - address: 10.34.61.145 - override_iptables: true - -# Deploy hosted engine host -- ovirt_host: - cluster: Default - name: myhost2 - password: secret - address: 10.34.61.145 - override_iptables: true - hosted_engine: deploy - -# Maintenance -- ovirt_host: - state: maintenance - name: myhost - -# Restart host using power management: -- ovirt_host: - state: restarted - name: myhost - -# Upgrade host -- ovirt_host: - state: upgraded - name: myhost - -# discover iscsi targets -- ovirt_host: - state: iscsidiscover - name: myhost - iscsi: - username: iscsi_user - password: secret - address: 10.34.61.145 - port: 3260 - - -# login to iscsi targets -- ovirt_host: - state: iscsilogin - name: myhost - iscsi: - username: iscsi_user - password: secret - address: 10.34.61.145 - target: "iqn.2015-07.com.mlipchuk2.redhat:444" - port: 3260 - - -# Reinstall host using public key -- ovirt_host: - state: reinstalled - name: myhost - public_key: true - -# Remove host -- ovirt_host: - state: absent - name: myhost - force: True - -# Change host Name -- ovirt_host: - id: 00000000-0000-0000-0000-000000000000 - name: "new host name" -''' - -RETURN = ''' -id: - description: ID of the host which is managed - returned: On success if host is found. - type: str - sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c -host: - description: "Dictionary of all the host attributes. Host attributes can be found on your oVirt/RHV instance - at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/host." - returned: On success if host is found. - type: dict -iscsi_targets: - description: "List of host iscsi targets" - returned: On success if host is found and state is iscsidiscover. - type: list -''' - -import time -import traceback - -try: - import ovirtsdk4.types as otypes - - from ovirtsdk4.types import HostStatus as hoststate -except ImportError: - pass - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ovirt import ( - BaseModule, - check_sdk, - create_connection, - equal, - get_id_by_name, - ovirt_full_argument_spec, - wait, -) - - -class HostsModule(BaseModule): - - def build_entity(self): - return otypes.Host( - id=self._module.params.get('id'), - name=self.param('name'), - cluster=otypes.Cluster( - name=self.param('cluster') - ) if self.param('cluster') else None, - comment=self.param('comment'), - address=self.param('address'), - root_password=self.param('password'), - ssh=otypes.Ssh( - authentication_method=otypes.SshAuthenticationMethod.PUBLICKEY, - ) if self.param('public_key') else None, - spm=otypes.Spm( - priority=self.param('spm_priority'), - ) if self.param('spm_priority') else None, - override_iptables=self.param('override_iptables'), - display=otypes.Display( - address=self.param('override_display'), - ) if self.param('override_display') else None, - os=otypes.OperatingSystem( - custom_kernel_cmdline=' '.join(self.param('kernel_params')), - ) if self.param('kernel_params') else None, - power_management=otypes.PowerManagement( - enabled=self.param('power_management_enabled'), - kdump_detection=self.param('kdump_integration') == 'enabled', - ) if self.param('power_management_enabled') is not None or self.param('kdump_integration') else None, - ) - - def update_check(self, entity): - kernel_params = self.param('kernel_params') - return ( - equal(self.param('comment'), entity.comment) and - equal(self.param('kdump_integration'), 'enabled' if entity.power_management.kdump_detection else 'disabled') and - equal(self.param('spm_priority'), entity.spm.priority) and - equal(self.param('name'), entity.name) and - equal(self.param('power_management_enabled'), entity.power_management.enabled) and - equal(self.param('override_display'), getattr(entity.display, 'address', None)) and - equal( - sorted(kernel_params) if kernel_params else None, - sorted(entity.os.custom_kernel_cmdline.split(' ')) - ) - ) - - def pre_remove(self, entity): - self.action( - entity=entity, - action='deactivate', - action_condition=lambda h: h.status != hoststate.MAINTENANCE, - wait_condition=lambda h: h.status == hoststate.MAINTENANCE, - ) - - def post_reinstall(self, host): - wait( - service=self._service.service(host.id), - condition=lambda h: h.status != hoststate.MAINTENANCE, - fail_condition=failed_state, - wait=self.param('wait'), - timeout=self.param('timeout'), - ) - - def failed_state_after_reinstall(self, host, count=0): - if host.status in [ - hoststate.ERROR, - hoststate.INSTALL_FAILED, - hoststate.NON_OPERATIONAL, - ]: - return True - - # If host is in non-responsive state after upgrade/install - # let's wait for few seconds and re-check again the state: - if host.status == hoststate.NON_RESPONSIVE: - if count <= 3: - time.sleep(20) - return self.failed_state_after_reinstall( - self._service.service(host.id).get(), - count + 1, - ) - else: - return True - - return False - - -def failed_state(host): - return host.status in [ - hoststate.ERROR, - hoststate.INSTALL_FAILED, - hoststate.NON_RESPONSIVE, - hoststate.NON_OPERATIONAL, - ] - - -def control_state(host_module): - host = host_module.search_entity() - if host is None: - return - - state = host_module._module.params['state'] - host_service = host_module._service.service(host.id) - if failed_state(host): - # In case host is in INSTALL_FAILED status, we can reinstall it: - if hoststate.INSTALL_FAILED == host.status and state != 'reinstalled': - raise Exception( - "Not possible to manage host '%s' in state '%s'." % ( - host.name, - host.status - ) - ) - elif host.status in [ - hoststate.REBOOT, - hoststate.CONNECTING, - hoststate.INITIALIZING, - hoststate.INSTALLING, - hoststate.INSTALLING_OS, - ]: - wait( - service=host_service, - condition=lambda host: host.status == hoststate.UP, - fail_condition=failed_state, - ) - elif host.status == hoststate.PREPARING_FOR_MAINTENANCE: - wait( - service=host_service, - condition=lambda host: host.status == hoststate.MAINTENANCE, - fail_condition=failed_state, - ) - - return host - - -def main(): - argument_spec = ovirt_full_argument_spec( - state=dict( - choices=[ - 'present', 'absent', 'maintenance', 'upgraded', 'started', - 'restarted', 'stopped', 'reinstalled', 'iscsidiscover', 'iscsilogin' - ], - default='present', - ), - name=dict(required=True), - id=dict(default=None), - comment=dict(default=None), - cluster=dict(default=None), - address=dict(default=None), - password=dict(default=None, no_log=True), - public_key=dict(default=False, type='bool', aliases=['ssh_public_key']), - kdump_integration=dict(default=None, choices=['enabled', 'disabled']), - spm_priority=dict(default=None, type='int'), - override_iptables=dict(default=None, type='bool'), - force=dict(default=False, type='bool'), - timeout=dict(default=600, type='int'), - override_display=dict(default=None), - kernel_params=dict(default=None, type='list'), - hosted_engine=dict(default=None, choices=['deploy', 'undeploy']), - power_management_enabled=dict(default=None, type='bool'), - activate=dict(default=True, type='bool'), - iscsi=dict(default=None, type='dict'), - check_upgrade=dict(default=True, type='bool'), - reboot_after_upgrade=dict(default=True, type='bool'), - ) - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'iscsidiscover', ['iscsi']], - ['state', 'iscsilogin', ['iscsi']] - ] - ) - - check_sdk(module) - - try: - auth = module.params.pop('auth') - connection = create_connection(auth) - hosts_service = connection.system_service().hosts_service() - hosts_module = HostsModule( - connection=connection, - module=module, - service=hosts_service, - ) - - state = module.params['state'] - host = control_state(hosts_module) - if state == 'present': - ret = hosts_module.create( - deploy_hosted_engine=( - module.params.get('hosted_engine') == 'deploy' - ) if module.params.get('hosted_engine') is not None else None, - result_state=hoststate.UP if host is None else None, - fail_condition=hosts_module.failed_state_after_reinstall if host is None else lambda h: False, - ) - if module.params['activate'] and host is not None: - ret = hosts_module.action( - action='activate', - action_condition=lambda h: h.status != hoststate.UP, - wait_condition=lambda h: h.status == hoststate.UP, - fail_condition=failed_state, - ) - elif state == 'absent': - ret = hosts_module.remove() - elif state == 'maintenance': - hosts_module.action( - action='deactivate', - action_condition=lambda h: h.status != hoststate.MAINTENANCE, - wait_condition=lambda h: h.status == hoststate.MAINTENANCE, - fail_condition=failed_state, - ) - ret = hosts_module.create() - elif state == 'upgraded': - result_state = hoststate.MAINTENANCE if host.status == hoststate.MAINTENANCE else hoststate.UP - events_service = connection.system_service().events_service() - last_event = events_service.list(max=1)[0] - - if module.params['check_upgrade']: - hosts_module.action( - action='upgrade_check', - action_condition=lambda host: not host.update_available, - wait_condition=lambda host: host.update_available or ( - len([ - event - for event in events_service.list( - from_=int(last_event.id), - search='type=885', - # Uncomment when 4.1 is EOL, and remove the cond: - # if host.name in event.description - # search='type=885 and host.name=%s' % host.name, - ) if host.name in event.description - ]) > 0 - ), - fail_condition=lambda host: len([ - event - for event in events_service.list( - from_=int(last_event.id), - search='type=839 or type=887 and host.name=%s' % host.name, - ) - ]) > 0, - ) - # Set to False, because upgrade_check isn't 'changing' action: - hosts_module._changed = False - ret = hosts_module.action( - action='upgrade', - action_condition=lambda h: h.update_available, - wait_condition=lambda h: h.status == result_state, - post_action=lambda h: time.sleep(module.params['poll_interval']), - fail_condition=lambda h: hosts_module.failed_state_after_reinstall(h) or ( - len([ - event - for event in events_service.list( - from_=int(last_event.id), - # Fail upgrade if migration fails: - # 17: Failed to switch Host to Maintenance mode - # 65, 140: Migration failed - # 166: No available host was found to migrate VM - search='type=65 or type=140 or type=166 or type=17', - ) if host.name in event.description - ]) > 0 - ), - reboot=module.params['reboot_after_upgrade'], - ) - elif state == 'iscsidiscover': - host_id = get_id_by_name(hosts_service, module.params['name']) - iscsi_param = module.params['iscsi'] - iscsi_targets = hosts_service.service(host_id).iscsi_discover( - iscsi=otypes.IscsiDetails( - port=int(iscsi_param.get('port', 3260)), - username=iscsi_param.get('username'), - password=iscsi_param.get('password'), - address=iscsi_param.get('address'), - ), - ) - ret = { - 'changed': False, - 'id': host_id, - 'iscsi_targets': iscsi_targets, - } - elif state == 'iscsilogin': - host_id = get_id_by_name(hosts_service, module.params['name']) - iscsi_param = module.params['iscsi'] - ret = hosts_module.action( - action='iscsi_login', - iscsi=otypes.IscsiDetails( - port=int(iscsi_param.get('port', 3260)), - username=iscsi_param.get('username'), - password=iscsi_param.get('password'), - address=iscsi_param.get('address'), - target=iscsi_param.get('target'), - ), - ) - elif state == 'started': - ret = hosts_module.action( - action='fence', - action_condition=lambda h: h.status == hoststate.DOWN, - wait_condition=lambda h: h.status in [hoststate.UP, hoststate.MAINTENANCE], - fail_condition=hosts_module.failed_state_after_reinstall, - fence_type='start', - ) - elif state == 'stopped': - hosts_module.action( - action='deactivate', - action_condition=lambda h: h.status not in [hoststate.MAINTENANCE, hoststate.DOWN], - wait_condition=lambda h: h.status in [hoststate.MAINTENANCE, hoststate.DOWN], - fail_condition=failed_state, - ) - ret = hosts_module.action( - action='fence', - action_condition=lambda h: h.status != hoststate.DOWN, - wait_condition=lambda h: h.status == hoststate.DOWN if module.params['wait'] else True, - fail_condition=failed_state, - fence_type='stop', - ) - elif state == 'restarted': - ret = hosts_module.action( - action='fence', - wait_condition=lambda h: h.status == hoststate.UP, - fail_condition=hosts_module.failed_state_after_reinstall, - fence_type='restart', - ) - elif state == 'reinstalled': - # Deactivate host if not in maintanence: - hosts_module.action( - action='deactivate', - action_condition=lambda h: h.status not in [hoststate.MAINTENANCE, hoststate.DOWN], - wait_condition=lambda h: h.status in [hoststate.MAINTENANCE, hoststate.DOWN], - fail_condition=failed_state, - ) - - # Reinstall host: - hosts_module.action( - action='install', - action_condition=lambda h: h.status == hoststate.MAINTENANCE, - post_action=hosts_module.post_reinstall, - wait_condition=lambda h: h.status == hoststate.MAINTENANCE, - fail_condition=hosts_module.failed_state_after_reinstall, - host=otypes.Host( - override_iptables=module.params['override_iptables'], - ) if module.params['override_iptables'] else None, - root_password=module.params['password'], - ssh=otypes.Ssh( - authentication_method=otypes.SshAuthenticationMethod.PUBLICKEY, - ) if module.params['public_key'] else None, - deploy_hosted_engine=( - module.params.get('hosted_engine') == 'deploy' - ) if module.params.get('hosted_engine') is not None else None, - undeploy_hosted_engine=( - module.params.get('hosted_engine') == 'undeploy' - ) if module.params.get('hosted_engine') is not None else None, - ) - - # Activate host after reinstall: - ret = hosts_module.action( - action='activate', - action_condition=lambda h: h.status == hoststate.MAINTENANCE, - wait_condition=lambda h: h.status == hoststate.UP, - fail_condition=failed_state, - ) - module.exit_json(**ret) - except Exception as e: - module.fail_json(msg=str(e), exception=traceback.format_exc()) - finally: - connection.close(logout=auth.get('token') is None) - - -if __name__ == "__main__": - main() diff --git a/library/ovirt_job_29.py b/library/ovirt_job_29.py deleted file mode 100644 index 5a96d7f..0000000 --- a/library/ovirt_job_29.py +++ /dev/null @@ -1,235 +0,0 @@ -#!/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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ovirt_job -short_description: Module to manage jobs in oVirt/RHV -version_added: "2.9" -author: "Martin Necas (@mnecas)" -description: - - "This module manage jobs in oVirt/RHV. It can also manage steps of the job." -options: - description: - description: - - "Description of the job." - required: true - state: - description: - - "Should the job be C(present)/C(absent)/C(failed)." - - "C(started) is alias for C(present). C(finished) is alias for C(absent). Same in the steps." - - "Note when C(finished)/C(failed) it will finish/fail all steps." - choices: ['present', 'absent', 'started', 'finished', 'failed'] - default: present - steps: - description: - - "The steps of the job." - suboptions: - description: - description: - - "Description of the step." - required: true - state: - description: - - "Should the step be present/absent/failed." - - "Note when one step fail whole job will fail" - - "Note when all steps are finished it will finish job." - choices: ['present', 'absent', 'started', 'finished', 'failed'] - default: present - type: list -extends_documentation_fragment: ovirt -''' - -EXAMPLES = ''' -# Examples don't contain auth parameter for simplicity, -# look at ovirt_auth module to see how to reuse authentication: - -- name: Create job with two steps - ovirt_job: - description: job_name - steps: - - description: step_name_A - - description: step_name_B - -- name: Finish one step - ovirt_job: - description: job_name - steps: - - description: step_name_A - state: finished - -- name: When you fail one step whole job will stop - ovirt_job: - description: job_name - steps: - - description: step_name_B - state: failed - -- name: Finish all steps - ovirt_job: - description: job_name - state: finished -''' - -RETURN = ''' -id: - description: ID of the job which is managed - returned: On success if job is found. - type: str - sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c -job: - description: "Dictionary of all the job attributes. Job attributes can be found on your oVirt/RHV instance - at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/job." - returned: On success if job is found. - type: dict -''' - -import traceback - -try: - import ovirtsdk4.types as otypes -except ImportError: - pass - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ovirt import ( - check_sdk, - create_connection, - equal, - get_id_by_name, - ovirt_full_argument_spec, - get_dict_of_struct, -) - - -def build_job(description): - return otypes.Job( - description=description, - status=otypes.JobStatus.STARTED, - external=True, - auto_cleared=True - ) - - -def build_step(description, job_id): - return otypes.Step( - description=description, - type=otypes.StepEnum.UNKNOWN, - job=otypes.Job( - id=job_id - ), - status=otypes.StepStatus.STARTED, - external=True, - ) - - -def attach_steps(module, job_id, jobs_service): - changed = False - steps_service = jobs_service.job_service(job_id).steps_service() - if module.params.get('steps'): - for step in module.params.get('steps'): - step_entity = get_entity(steps_service, step.get('description')) - step_state = step.get('state', 'present') - if step_state in ['present', 'started']: - if step_entity is None: - steps_service.add(build_step(step.get('description'), job_id)) - changed = True - if step_entity is not None and step_entity.status not in [otypes.StepStatus.FINISHED, otypes.StepStatus.FAILED]: - if step_state in ['absent', 'finished']: - steps_service.step_service(step_entity.id).end(succeeded=True) - changed = True - elif step_state == 'failed': - steps_service.step_service(step_entity.id).end(succeeded=False) - changed = True - return changed - - -def get_entity(service, description): - all_entities = service.list() - for entity in all_entities: - if entity.description == description and entity.status not in [otypes.StepStatus.FINISHED, otypes.JobStatus.FINISHED]: - return entity - - -def main(): - argument_spec = ovirt_full_argument_spec( - state=dict( - choices=['present', 'absent', 'started', 'finished', 'failed'], - default='present', - ), - description=dict(default=None), - steps=dict(default=None, type='list'), - ) - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=False, - ) - - check_sdk(module) - - try: - auth = module.params.pop('auth') - connection = create_connection(auth) - jobs_service = connection.system_service().jobs_service() - - state = module.params['state'] - job = get_entity(jobs_service, module.params['description']) - changed = False - if state in ['present', 'started']: - if job is None: - job = jobs_service.add(build_job(module.params['description'])) - changed = True - changed = attach_steps(module, job.id, jobs_service) or changed - - if job is not None and job.status not in [otypes.JobStatus.FINISHED, otypes.JobStatus.FAILED]: - if state in ['absent', 'finished']: - jobs_service.job_service(job.id).end(succeeded=True) - changed = True - - elif state == 'failed': - jobs_service.job_service(job.id).end(succeeded=False) - changed = True - - ret = { - 'changed': changed, - 'id': getattr(job, 'id', None), - 'job': get_dict_of_struct( - struct=job, - connection=connection, - fetch_nested=True, - attributes=module.params.get('nested_attributes'), - ), - } - - module.exit_json(**ret) - except Exception as e: - module.fail_json(msg=str(e), exception=traceback.format_exc()) - finally: - connection.close(logout=auth.get('token') is None) - - -if __name__ == "__main__": - main() diff --git a/library/ovirt_vm_28.py b/library/ovirt_vm_28.py deleted file mode 100644 index 247051b..0000000 --- a/library/ovirt_vm_28.py +++ /dev/null @@ -1,2507 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ovirt_vm -short_description: Module to manage Virtual Machines in oVirt/RHV -version_added: "2.2" -author: -- Ondra Machacek (@machacekondra) -description: - - This module manages whole lifecycle of the Virtual Machine(VM) in oVirt/RHV. - - Since VM can hold many states in oVirt/RHV, this see notes to see how the states of the VM are handled. -options: - name: - description: - - Name of 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 Virtual Machine to manage. - state: - description: - - Should the Virtual Machine be running/stopped/present/absent/suspended/next_run/registered/exported. - When C(state) is I(registered) and the unregistered VM's name - belongs to an already registered in engine VM in the same DC - then we fail to register the unregistered template. - - I(present) state will create/update VM and don't change its state if it already exists. - - I(running) state will create/update VM and start it. - - 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. - - I(exported) state will export the VM to export domain or as OVA. - - I(registered) is supported since 2.4. - choices: [ absent, next_run, present, registered, running, stopped, suspended, exported ] - default: present - cluster: - description: - - Name of the cluster, where Virtual Machine should be created. - - Required if creating VM. - allow_partial_import: - description: - - Boolean indication whether to allow partial registration of Virtual Machine when C(state) is registered. - type: bool - version_added: "2.4" - vnic_profile_mappings: - description: - - "Mapper which maps an external virtual NIC profile to one that exists in the engine when C(state) is registered. - vnic_profile is described by the following dictionary:" - suboptions: - source_network_name: - description: - - The network name of the source network. - source_profile_name: - description: - - The profile name related to the source network. - target_profile_id: - description: - - The id of the target profile id to be mapped to in the engine. - version_added: "2.5" - cluster_mappings: - description: - - "Mapper which maps cluster name between VM's OVF and the destination cluster this VM should be registered to, - relevant when C(state) is registered. - Cluster mapping is described by the following dictionary:" - suboptions: - source_name: - description: - - The name of the source cluster. - dest_name: - description: - - The name of the destination cluster. - version_added: "2.5" - role_mappings: - description: - - "Mapper which maps role name between VM's OVF and the destination role this VM should be registered to, - relevant when C(state) is registered. - Role mapping is described by the following dictionary:" - suboptions: - source_name: - description: - - The name of the source role. - dest_name: - description: - - The name of the destination role. - version_added: "2.5" - domain_mappings: - description: - - "Mapper which maps aaa domain name between VM's OVF and the destination aaa domain this VM should be registered to, - relevant when C(state) is registered. - The aaa domain mapping is described by the following dictionary:" - suboptions: - source_name: - description: - - The name of the source aaa domain. - dest_name: - description: - - The name of the destination aaa domain. - version_added: "2.5" - affinity_group_mappings: - description: - - "Mapper which maps affinty name between VM's OVF and the destination affinity this VM should be registered to, - relevant when C(state) is registered." - version_added: "2.5" - affinity_label_mappings: - description: - - "Mappper which maps affinity label name between VM's OVF and the destination label this VM should be registered to, - relevant when C(state) is registered." - version_added: "2.5" - lun_mappings: - description: - - "Mapper which maps lun between VM's OVF and the destination lun this VM should contain, relevant when C(state) is registered. - lun_mappings is described by the following dictionary: - - C(logical_unit_id): The logical unit number to identify a logical unit, - - C(logical_unit_port): The port being used to connect with the LUN disk. - - C(logical_unit_portal): The portal being used to connect with the LUN disk. - - C(logical_unit_address): The address of the block storage host. - - C(logical_unit_target): The iSCSI specification located on an iSCSI server - - C(logical_unit_username): Username to be used to connect to the block storage host. - - C(logical_unit_password): Password to be used to connect to the block storage host. - - C(storage_type): The storage type which the LUN reside on (iscsi or fcp)" - version_added: "2.5" - reassign_bad_macs: - description: - - "Boolean indication whether to reassign bad macs when C(state) is registered." - type: bool - version_added: "2.5" - 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. - template_version: - description: - - Version number of the template to be used for VM. - - By default the latest available version of the template is used. - version_added: "2.3" - use_latest_template_version: - description: - - Specify if latest template version should be used, when running a stateless VM. - - If this parameter is set to I(yes) stateless VM is created. - type: bool - version_added: "2.3" - storage_domain: - description: - - Name of the storage domain where all template disks should be created. - - This parameter is considered only when C(template) is provided. - - IMPORTANT - This parameter is not idempotent, if the VM exists and you specfiy different storage domain, - disk won't move. - version_added: "2.4" - disk_format: - description: - - Specify format of the disk. - - If C(cow) format is used, disk will by created as sparse, so space will be allocated for the volume as needed, also known as I(thin provision). - - If C(raw) format is used, disk storage will be allocated right away, also known as I(preallocated). - - Note that this option isn't idempotent as it's not currently possible to change format of the disk via API. - - This parameter is considered only when C(template) and C(storage domain) is provided. - choices: [ cow, raw ] - default: cow - version_added: "2.4" - 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. - memory_max: - description: - - Upper bound of virtual machine memory up to which memory hot-plug can be performed. - Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB). - - Default value is set by engine. - version_added: "2.5" - cpu_shares: - description: - - Set a CPU shares for this Virtual Machine. - - Default value is set by oVirt/RHV engine. - cpu_cores: - description: - - Number of virtual CPUs cores of the Virtual Machine. - - Default value is set by oVirt/RHV engine. - cpu_sockets: - description: - - Number of virtual CPUs sockets of the Virtual Machine. - - Default value is set by oVirt/RHV engine. - cpu_threads: - description: - - Number of virtual CPUs sockets of the Virtual Machine. - - Default value is set by oVirt/RHV engine. - version_added: "2.5" - type: - description: - - Type of the Virtual Machine. - - Default value is set by oVirt/RHV engine. - - I(high_performance) is supported since Ansible 2.5 and oVirt/RHV 4.2. - choices: [ desktop, server, high_performance ] - quota_id: - description: - - "Virtual Machine quota ID to be used for disk. By default quota is chosen by oVirt/RHV engine." - version_added: "2.5" - operating_system: - description: - - Operating system of the Virtual Machine. - - Default value is set by oVirt/RHV engine. - - "Possible values: debian_7, freebsd, freebsdx64, other, other_linux, - other_linux_ppc64, other_ppc64, rhel_3, rhel_4, rhel_4x64, rhel_5, rhel_5x64, - rhel_6, rhel_6x64, rhel_6_ppc64, rhel_7x64, rhel_7_ppc64, sles_11, sles_11_ppc64, - ubuntu_12_04, ubuntu_12_10, ubuntu_13_04, ubuntu_13_10, ubuntu_14_04, ubuntu_14_04_ppc64, - windows_10, windows_10x64, windows_2003, windows_2003x64, windows_2008, windows_2008x64, - windows_2008r2x64, windows_2008R2x64, windows_2012x64, windows_2012R2x64, windows_7, - windows_7x64, windows_8, windows_8x64, windows_xp" - boot_devices: - description: - - List of boot devices which should be used to boot. For example C([ cdrom, hd ]). - - Default value is set by oVirt/RHV engine. - choices: [ cdrom, hd, network ] - boot_menu: - description: - - "I(True) enable menu to select boot device, I(False) to disable it. By default is chosen by oVirt/RHV engine." - type: bool - version_added: "2.5" - usb_support: - description: - - "I(True) enable USB support, I(False) to disable it. By default is chosen by oVirt/RHV engine." - type: bool - version_added: "2.5" - serial_console: - description: - - "I(True) enable VirtIO serial console, I(False) to disable it. By default is chosen by oVirt/RHV engine." - type: bool - version_added: "2.5" - sso: - description: - - "I(True) enable Single Sign On by Guest Agent, I(False) to disable it. By default is chosen by oVirt/RHV engine." - type: bool - version_added: "2.5" - 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(yes) Virtual Machine will be set as highly available. - - If I(no) Virtual Machine won't be set as highly available. - - If no value is passed, default value is set by oVirt/RHV engine. - type: bool - high_availability_priority: - description: - - Indicates the priority of the virtual machine inside the run and migration queues. - Virtual machines with higher priorities will be started and migrated before virtual machines with lower - priorities. The value is an integer between 0 and 100. The higher the value, the higher the priority. - - If no value is passed, default value is set by oVirt/RHV engine. - version_added: "2.5" - lease: - description: - - Name of the storage domain this virtual machine lease reside on. - - NOTE - Supported since oVirt 4.1. - version_added: "2.4" - custom_compatibility_version: - description: - - "Enables a virtual machine to be customized to its own compatibility version. If - 'C(custom_compatibility_version)' is set, it overrides the cluster's compatibility version - for this particular virtual machine." - version_added: "2.7" - host_devices: - description: - - Single Root I/O Virtualization - technology that allows single device to expose multiple endpoints that can be passed to VMs - - host_devices is an list which contain dictinary with name and state of device - version_added: "2.7" - delete_protected: - description: - - If I(yes) Virtual Machine will be set as delete protected. - - If I(no) Virtual Machine won't be set as delete protected. - - If no value is passed, default value is set by oVirt/RHV engine. - type: bool - stateless: - description: - - If I(yes) Virtual Machine will be set as stateless. - - If I(no) Virtual Machine will be unset as stateless. - - If no value is passed, default value is set by oVirt/RHV engine. - type: bool - clone: - description: - - If I(yes) 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. - type: bool - default: 'no' - clone_permissions: - description: - - If I(yes) 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. - type: bool - default: 'no' - 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. - type: bool - default: 'no' - nics: - description: - - List of NICs, which should be attached to Virtual Machine. NIC is described by following dictionary. - suboptions: - name: - description: - - Name of the NIC. - profile_name: - description: - - Profile name where NIC should be attached. - interface: - description: - - Type of the network interface. - choices: ['virtio', 'e1000', 'rtl8139'] - default: 'virtio' - mac_address: - description: - - Custom MAC address of the network interface, by default it's obtained from MAC pool. - - "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. - suboptions: - name: - description: - - Name of the disk. Either C(name) or C(id) is required. - id: - description: - - ID of the disk. Either C(name) or C(id) is required. - interface: - description: - - Interface of the disk. - choices: ['virtio', 'IDE'] - default: 'virtio' - bootable: - description: - - I(True) if the disk should be bootable, default is non bootable. - type: bool - activate: - description: - - I(True) if the disk should be activated, default is activated. - - "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." - type: bool - sysprep: - description: - - Dictionary with values for Windows Virtual Machine initialization using sysprep. - suboptions: - host_name: - description: - - Hostname to be set to Virtual Machine when deployed. - active_directory_ou: - description: - - Active Directory Organizational Unit, to be used for login of user. - org_name: - description: - - Organization name to be set to Windows Virtual Machine. - domain: - description: - - Domain to be set to Windows Virtual Machine. - timezone: - description: - - Timezone to be set to Windows Virtual Machine. - ui_language: - description: - - UI language of the Windows Virtual Machine. - system_locale: - description: - - System localization of the Windows Virtual Machine. - input_locale: - description: - - Input localization of the Windows Virtual Machine. - windows_license_key: - description: - - License key to be set to Windows Virtual Machine. - user_name: - description: - - Username to be used for set password to Windows Virtual Machine. - root_password: - description: - - 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. - suboptions: - host_name: - description: - - Hostname to be set to Virtual Machine when deployed. - timezone: - description: - - Timezone to be set to Virtual Machine when deployed. - user_name: - description: - - Username to be used to set password to Virtual Machine when deployed. - root_password: - description: - - Password to be set for user specified by C(user_name) parameter. - authorized_ssh_keys: - description: - - Use this SSH keys to login to Virtual Machine. - regenerate_ssh_keys: - description: - - If I(True) SSH keys will be regenerated on Virtual Machine. - type: bool - custom_script: - description: - - Cloud-init script which will be executed on Virtual Machine when deployed. - - This is appended to the end of the cloud-init script generated by any other options. - dns_servers: - description: - - DNS servers to be configured on Virtual Machine. - dns_search: - description: - - DNS search domains to be configured on Virtual Machine. - nic_boot_protocol: - description: - - Set boot protocol of the network interface of Virtual Machine. - choices: ['none', 'dhcp', 'static'] - nic_ip_address: - description: - - If boot protocol is static, set this IP address to network interface of Virtual Machine. - nic_netmask: - description: - - If boot protocol is static, set this netmask to network interface of Virtual Machine. - nic_gateway: - description: - - If boot protocol is static, set this gateway to network interface of Virtual Machine. - nic_name: - description: - - Set name to network interface of Virtual Machine. - nic_on_boot: - description: - - If I(True) network interface will be set to start on boot. - type: bool - cloud_init_nics: - description: - - List of dictionaries representing network interfaces to be setup by cloud init. - - This option is used, when user needs to setup more network interfaces via cloud init. - - If one network interface is enough, user should use C(cloud_init) I(nic_*) parameters. C(cloud_init) I(nic_*) parameters - are merged with C(cloud_init_nics) parameters. - suboptions: - nic_boot_protocol: - description: - - Set boot protocol of the network interface of Virtual Machine. Can be one of C(none), C(dhcp) or C(static). - nic_ip_address: - description: - - If boot protocol is static, set this IP address to network interface of Virtual Machine. - nic_netmask: - description: - - If boot protocol is static, set this netmask to network interface of Virtual Machine. - nic_gateway: - description: - - If boot protocol is static, set this gateway to network interface of Virtual Machine. - nic_name: - description: - - Set name to network interface of Virtual Machine. - nic_on_boot: - description: - - If I(True) network interface will be set to start on boot. - type: bool - version_added: "2.3" - cloud_init_persist: - description: - - "If I(yes) the C(cloud_init) or C(sysprep) parameters will be saved for the virtual machine - and the virtual machine won't be started as run-once." - type: bool - version_added: "2.5" - aliases: [ 'sysprep_persist' ] - default: 'no' - kernel_params_persist: - description: - - "If I(true) C(kernel_params), C(initrd_path) and C(kernel_path) will persist in virtual machine configuration, - if I(False) it will be used for run once." - type: bool - version_added: "2.8" - kernel_path: - description: - - Path to a kernel image used to boot the virtual machine. - - Kernel image must be stored on either the ISO domain or on the host's storage. - version_added: "2.3" - initrd_path: - description: - - Path to an initial ramdisk to be used with the kernel specified by C(kernel_path) option. - - Ramdisk image must be stored on either the ISO domain or on the host's storage. - version_added: "2.3" - kernel_params: - description: - - Kernel command line parameters (formatted as string) to be used with the kernel specified by C(kernel_path) option. - version_added: "2.3" - instance_type: - description: - - Name of virtual machine's hardware configuration. - - By default no instance type is used. - version_added: "2.3" - description: - description: - - Description of the Virtual Machine. - version_added: "2.3" - comment: - description: - - Comment of the Virtual Machine. - version_added: "2.3" - timezone: - description: - - Sets time zone offset of the guest hardware clock. - - For example C(Etc/GMT) - version_added: "2.3" - serial_policy: - description: - - Specify a serial number policy for the Virtual Machine. - - Following options are supported. - - C(vm) - Sets the Virtual Machine's UUID as its serial number. - - C(host) - Sets the host's UUID as the Virtual Machine's serial number. - - C(custom) - Allows you to specify a custom serial number in C(serial_policy_value). - choices: ['vm', 'host', 'custom'] - version_added: "2.3" - serial_policy_value: - description: - - Allows you to specify a custom serial number. - - This parameter is used only when C(serial_policy) is I(custom). - version_added: "2.3" - vmware: - description: - - Dictionary of values to be used to connect to VMware and import - a virtual machine to oVirt. - suboptions: - username: - description: - - The username to authenticate against the VMware. - password: - description: - - The password to authenticate against the VMware. - url: - description: - - The URL to be passed to the I(virt-v2v) tool for conversion. - - For example I(vpx://wmware_user@vcenter-host/DataCenter/Cluster/esxi-host?no_verify=1) - drivers_iso: - description: - - The name of the ISO containing drivers that can be used during the I(virt-v2v) conversion process. - sparse: - description: - - Specifies the disk allocation policy of the resulting virtual machine. I(true) for sparse, I(false) for preallocated. - type: bool - default: true - storage_domain: - description: - - Specifies the target storage domain for converted disks. This is required parameter. - version_added: "2.3" - xen: - description: - - Dictionary of values to be used to connect to XEN and import - a virtual machine to oVirt. - suboptions: - url: - description: - - The URL to be passed to the I(virt-v2v) tool for conversion. - - For example I(xen+ssh://root@zen.server). This is required parameter. - drivers_iso: - description: - - The name of the ISO containing drivers that can be used during the I(virt-v2v) conversion process. - sparse: - description: - - Specifies the disk allocation policy of the resulting virtual machine. I(true) for sparse, I(false) for preallocated. - type: bool - default: true - storage_domain: - description: - - Specifies the target storage domain for converted disks. This is required parameter. - version_added: "2.3" - kvm: - description: - - Dictionary of values to be used to connect to kvm and import - a virtual machine to oVirt. - suboptions: - name: - description: - - The name of the KVM virtual machine. - username: - description: - - The username to authenticate against the KVM. - password: - description: - - The password to authenticate against the KVM. - url: - description: - - The URL to be passed to the I(virt-v2v) tool for conversion. - - For example I(qemu:///system). This is required parameter. - drivers_iso: - description: - - The name of the ISO containing drivers that can be used during the I(virt-v2v) conversion process. - sparse: - description: - - Specifies the disk allocation policy of the resulting virtual machine. I(true) for sparse, I(false) for preallocated. - type: bool - default: true - storage_domain: - description: - - Specifies the target storage domain for converted disks. This is required parameter. - version_added: "2.3" - cpu_mode: - description: - - "CPU mode of the virtual machine. It can be some of the following: I(host_passthrough), I(host_model) or I(custom)." - - "For I(host_passthrough) CPU type you need to set C(placement_policy) to I(pinned)." - - "If no value is passed, default value is set by oVirt/RHV engine." - version_added: "2.5" - placement_policy: - description: - - "The configuration of the virtual machine's placement policy." - - "If no value is passed, default value is set by oVirt/RHV engine." - - "Placement policy can be one of the following values:" - suboptions: - migratable: - description: - - "Allow manual and automatic migration." - pinned: - description: - - "Do not allow migration." - user_migratable: - description: - - "Allow manual migration only." - version_added: "2.5" - ticket: - description: - - "If I(true), in addition return I(remote_vv_file) inside I(vm) dictionary, which contains compatible - content for remote-viewer application. Works only C(state) is I(running)." - version_added: "2.7" - type: bool - cpu_pinning: - description: - - "CPU Pinning topology to map virtual machine CPU to host CPU." - - "CPU Pinning topology is a list of dictionary which can have following values:" - suboptions: - cpu: - description: - - "Number of the host CPU." - vcpu: - description: - - "Number of the virtual machine CPU." - version_added: "2.5" - soundcard_enabled: - description: - - "If I(true), the sound card is added to the virtual machine." - type: bool - version_added: "2.5" - smartcard_enabled: - description: - - "If I(true), use smart card authentication." - type: bool - version_added: "2.5" - io_threads: - description: - - "Number of IO threads used by virtual machine. I(0) means IO threading disabled." - version_added: "2.5" - ballooning_enabled: - description: - - "If I(true), use memory ballooning." - - "Memory balloon is a guest device, which may be used to re-distribute / reclaim the host memory - based on VM needs in a dynamic way. In this way it's possible to create memory over commitment states." - type: bool - version_added: "2.5" - numa_tune_mode: - description: - - "Set how the memory allocation for NUMA nodes of this VM is applied (relevant if NUMA nodes are set for this VM)." - - "It can be one of the following: I(interleave), I(preferred) or I(strict)." - - "If no value is passed, default value is set by oVirt/RHV engine." - choices: ['interleave', 'preferred', 'strict'] - version_added: "2.6" - numa_nodes: - description: - - "List of vNUMA Nodes to set for this VM and pin them to assigned host's physical NUMA node." - - "Each vNUMA node is described by following dictionary:" - suboptions: - index: - description: - - "The index of this NUMA node (mandatory)." - memory: - description: - - "Memory size of the NUMA node in MiB (mandatory)." - cores: - description: - - "list of VM CPU cores indexes to be included in this NUMA node (mandatory)." - numa_node_pins: - description: - - "list of physical NUMA node indexes to pin this virtual NUMA node to." - version_added: "2.6" - rng_device: - description: - - "Random number generator (RNG). You can choose of one the following devices I(urandom), I(random) or I(hwrng)." - - "In order to select I(hwrng), you must have it enabled on cluster first." - - "/dev/urandom is used for cluster version >= 4.1, and /dev/random for cluster version <= 4.0" - version_added: "2.5" - custom_properties: - description: - - "Properties sent to VDSM to configure various hooks." - - "Custom properties is a list of dictionary which can have following values:" - suboptions: - name: - description: - - "Name of the custom property. For example: I(hugepages), I(vhost), I(sap_agent), etc." - regexp: - description: - - "Regular expression to set for custom property." - value: - description: - - "Value to set for custom property." - version_added: "2.5" - watchdog: - description: - - "Assign watchdog device for the virtual machine." - - "Watchdogs is a dictionary which can have following values:" - suboptions: - model: - description: - - "Model of the watchdog device. For example: I(i6300esb), I(diag288) or I(null)." - action: - description: - - "Watchdog action to be performed when watchdog is triggered. For example: I(none), I(reset), I(poweroff), I(pause) or I(dump)." - version_added: "2.5" - graphical_console: - description: - - "Assign graphical console to the virtual machine." - suboptions: - headless_mode: - description: - - If I(true) disable the graphics console for this virtual machine. - type: bool - protocol: - description: - - Graphical protocol, a list of I(spice), I(vnc), or both. - version_added: "2.5" - exclusive: - description: - - "When C(state) is I(exported) this parameter indicates if the existing VM with the - same name should be overwritten." - version_added: "2.8" - type: bool - export_domain: - description: - - "When C(state) is I(exported)this parameter specifies the name of the export storage domain." - version_added: "2.8" - export_ova: - description: - - Dictionary of values to be used to export VM as OVA. - suboptions: - host: - description: - - The name of the destination host where the OVA has to be exported. - directory: - description: - - The name of the directory where the OVA has to be exported. - filename: - description: - - The name of the exported OVA file. - version_added: "2.8" - force_migrate: - description: - - "If I(true), the VM will migrate even if it is defined as non-migratable." - version_added: "2.8" - type: bool - migrate: - description: - - "If I(true), the VM will migrate to any available host." - version_added: "2.8" - type: bool - next_run: - description: - - "If I(true), the update will not be applied to the VM immediately and will be only applied when virtual machine is restarted." - - NOTE - If there are multiple next run configuration changes on the VM, the first change may get reverted if this option is not passed. - version_added: "2.8" - type: bool - -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: - -- name: Creates a new Virtual Machine from template named 'rhel7_template' - ovirt_vm: - state: present - name: myvm - template: rhel7_template - cluster: mycluster - -- name: Register VM - ovirt_vm: - state: registered - storage_domain: mystorage - cluster: mycluster - name: myvm - -- name: Register VM using id - ovirt_vm: - state: registered - storage_domain: mystorage - cluster: mycluster - id: 1111-1111-1111-1111 - -- name: Register VM, allowing partial import - ovirt_vm: - state: registered - storage_domain: mystorage - allow_partial_import: "True" - cluster: mycluster - id: 1111-1111-1111-1111 - -- name: Register VM with vnic profile mappings and reassign bad macs - ovirt_vm: - state: registered - storage_domain: mystorage - cluster: mycluster - id: 1111-1111-1111-1111 - vnic_profile_mappings: - - source_network_name: mynetwork - source_profile_name: mynetwork - target_profile_id: 3333-3333-3333-3333 - - source_network_name: mynetwork2 - source_profile_name: mynetwork2 - target_profile_id: 4444-4444-4444-4444 - reassign_bad_macs: "True" - -- name: Register VM with mappings - ovirt_vm: - state: registered - storage_domain: mystorage - cluster: mycluster - id: 1111-1111-1111-1111 - role_mappings: - - source_name: Role_A - dest_name: Role_B - domain_mappings: - - source_name: Domain_A - dest_name: Domain_B - lun_mappings: - - source_storage_type: iscsi - source_logical_unit_id: 1IET_000d0001 - source_logical_unit_port: 3260 - source_logical_unit_portal: 1 - source_logical_unit_address: 10.34.63.203 - source_logical_unit_target: iqn.2016-08-09.brq.str-01:omachace - dest_storage_type: iscsi - dest_logical_unit_id: 1IET_000d0002 - dest_logical_unit_port: 3260 - dest_logical_unit_portal: 1 - dest_logical_unit_address: 10.34.63.204 - dest_logical_unit_target: iqn.2016-08-09.brq.str-02:omachace - affinity_group_mappings: - - source_name: Affinity_A - dest_name: Affinity_B - affinity_label_mappings: - - source_name: Label_A - dest_name: Label_B - cluster_mappings: - - source_name: cluster_A - dest_name: cluster_B - -- name: Creates a stateless VM which will always use latest template version - ovirt_vm: - name: myvm - template: rhel7 - cluster: mycluster - use_latest_template_version: true - -# 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_vm: - 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 - -# Change VM Name -- ovirt_vm: - id: 00000000-0000-0000-0000-000000000000 - name: "new_vm_name" - -- name: Run VM with cloud init - ovirt_vm: - name: rhel7 - template: rhel7 - cluster: Default - memory: 1GiB - high_availability: true - high_availability_priority: 50 # Available from Ansible 2.5 - 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 - -- name: Run VM with cloud init, with multiple network interfaces - ovirt_vm: - name: rhel7_4 - template: rhel7 - cluster: mycluster - cloud_init_nics: - - nic_name: eth0 - nic_boot_protocol: dhcp - nic_on_boot: true - - nic_name: eth1 - nic_boot_protocol: static - nic_ip_address: 10.34.60.86 - nic_netmask: 255.255.252.0 - nic_gateway: 10.34.63.254 - nic_on_boot: true - -- name: Run VM with sysprep - ovirt_vm: - name: windows2012R2_AD - template: windows2012R2 - cluster: Default - memory: 3GiB - high_availability: true - sysprep: - host_name: windowsad.example.com - user_name: Administrator - root_password: SuperPassword123 - -- name: Migrate/Run VM to/on host named 'host1' - ovirt_vm: - state: running - name: myvm - host: host1 - -- name: Migrate VM to any available host - ovirt_vm: - state: running - name: myvm - migrate: true - -- name: Change VMs CD - ovirt_vm: - name: myvm - cd_iso: drivers.iso - -- name: Eject VMs CD - ovirt_vm: - name: myvm - cd_iso: '' - -- name: Boot VM from CD - ovirt_vm: - name: myvm - cd_iso: centos7_x64.iso - boot_devices: - - cdrom - -- name: Stop vm - ovirt_vm: - state: stopped - name: myvm - -- name: Upgrade memory to already created VM - ovirt_vm: - name: myvm - memory: 4GiB - -- name: Hot plug memory to already created and running VM (VM won't be restarted) - ovirt_vm: - name: myvm - memory: 4GiB - -# Create/update a VM to run with two vNUMA nodes and pin them to physical NUMA nodes as follows: -# vnuma index 0-> numa index 0, vnuma index 1-> numa index 1 -- name: Create a VM to run with two vNUMA nodes - ovirt_vm: - name: myvm - cluster: mycluster - numa_tune_mode: "interleave" - numa_nodes: - - index: 0 - cores: [0] - memory: 20 - numa_node_pins: [0] - - index: 1 - cores: [1] - memory: 30 - numa_node_pins: [1] - -- name: Update an existing VM to run without previously created vNUMA nodes (i.e. remove all vNUMA nodes+NUMA pinning setting) - ovirt_vm: - name: myvm - cluster: mycluster - state: "present" - numa_tune_mode: "interleave" - numa_nodes: - - index: -1 - -# 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_vm: - state: next_run - name: myvm - boot_devices: - - network - -- name: Import virtual machine from VMware - ovirt_vm: - state: stopped - cluster: mycluster - name: vmware_win10 - timeout: 1800 - poll_interval: 30 - vmware: - url: vpx://user@1.2.3.4/Folder1/Cluster1/2.3.4.5?no_verify=1 - name: windows10 - storage_domain: mynfs - username: user - password: password - -- name: Create vm from template and create all disks on specific storage domain - ovirt_vm: - name: vm_test - cluster: mycluster - template: mytemplate - storage_domain: mynfs - nics: - - name: nic1 - -- name: Remove VM, if VM is running it will be stopped - ovirt_vm: - state: absent - name: myvm - -# Defining a specific quota for a VM: -# Since Ansible 2.5 -- ovirt_quotas_facts: - data_center: Default - name: myquota -- ovirt_vm: - name: myvm - sso: False - boot_menu: True - usb_support: True - serial_console: True - quota_id: "{{ ovirt_quotas[0]['id'] }}" - -- name: Create a VM that has the console configured for both Spice and VNC - ovirt_vm: - name: myvm - template: mytemplate - cluster: mycluster - graphical_console: - protocol: - - spice - - vnc - -# Execute remote viever to VM -- block: - - name: Create a ticket for console for a running VM - ovirt_vms: - name: myvm - ticket: true - state: running - register: myvm - - - name: Save ticket to file - copy: - content: "{{ myvm.vm.remote_vv_file }}" - dest: ~/vvfile.vv - - - name: Run remote viewer with file - command: remote-viewer ~/vvfile.vv - -# Default value of host_device state is present -- name: Attach host devices to virtual machine - ovirt_vm: - name: myvm - host: myhost - placement_policy: pinned - host_devices: - - name: pci_0000_00_06_0 - - name: pci_0000_00_07_0 - state: absent - - name: pci_0000_00_08_0 - state: present - -- name: Export the VM as OVA - ovirt_vm: - name: myvm - state: exported - cluster: mycluster - export_ova: - host: myhost - filename: myvm.ova - directory: /tmp/ -''' - - -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/RHV instance - at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/vm. - Additionally when user sent ticket=true, this module will return also remote_vv_file - parameter in vm dictionary, which contains remote-viewer compatible file to open virtual - machine console. Please note that this file contains sensible information." - returned: On success if VM is found. - type: dict -''' -import traceback - -try: - import ovirtsdk4.types as otypes -except ImportError: - pass - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ovirt import ( - BaseModule, - check_params, - check_sdk, - convert_to_bytes, - create_connection, - equal, - get_dict_of_struct, - get_entity, - get_link_name, - get_id_by_name, - ovirt_full_argument_spec, - search_by_attributes, - search_by_name, - wait, -) - - -class VmsModule(BaseModule): - - def __init__(self, *args, **kwargs): - super(VmsModule, self).__init__(*args, **kwargs) - self._initialization = None - self._is_new = False - - def __get_template_with_version(self): - """ - oVirt/RHV in version 4.1 doesn't support search by template+version_number, - so we need to list all templates with specific name and then iterate - through it's version until we find the version we look for. - """ - template = None - templates_service = self._connection.system_service().templates_service() - if self.param('template'): - templates = templates_service.list( - search='name=%s and cluster=%s' % (self.param('template'), self.param('cluster')) - ) - if self.param('template_version'): - templates = [ - t for t in templates - if t.version.version_number == self.param('template_version') - ] - if not templates: - raise ValueError( - "Template with name '%s' and version '%s' in cluster '%s' was not found'" % ( - self.param('template'), - self.param('template_version'), - self.param('cluster') - ) - ) - template = sorted(templates, key=lambda t: t.version.version_number, reverse=True)[0] - elif self._is_new: - # If template isn't specified and VM is about to be created specify default template: - template = templates_service.template_service('00000000-0000-0000-0000-000000000000').get() - - return template - - def __get_storage_domain_and_all_template_disks(self, template): - - if self.param('template') is None: - return None - - if self.param('storage_domain') is None: - return None - - disks = list() - - for att in self._connection.follow_link(template.disk_attachments): - disks.append( - otypes.DiskAttachment( - disk=otypes.Disk( - id=att.disk.id, - format=otypes.DiskFormat(self.param('disk_format')), - storage_domains=[ - otypes.StorageDomain( - id=get_id_by_name( - self._connection.system_service().storage_domains_service(), - self.param('storage_domain') - ) - ) - ] - ) - ) - ) - - return disks - - def build_entity(self): - template = self.__get_template_with_version() - - disk_attachments = self.__get_storage_domain_and_all_template_disks(template) - - return otypes.Vm( - id=self.param('id'), - name=self.param('name'), - cluster=otypes.Cluster( - name=self.param('cluster') - ) if self.param('cluster') else None, - disk_attachments=disk_attachments, - template=otypes.Template( - id=template.id, - ) if template else None, - use_latest_template_version=self.param('use_latest_template_version'), - stateless=self.param('stateless') or self.param('use_latest_template_version'), - delete_protected=self.param('delete_protected'), - bios=( - otypes.Bios(boot_menu=otypes.BootMenu(enabled=self.param('boot_menu'))) - ) if self.param('boot_menu') is not None else None, - console=( - otypes.Console(enabled=self.param('serial_console')) - ) if self.param('serial_console') is not None else None, - usb=( - otypes.Usb(enabled=self.param('usb_support')) - ) if self.param('usb_support') is not None else None, - sso=( - otypes.Sso( - methods=[otypes.Method(id=otypes.SsoMethod.GUEST_AGENT)] if self.param('sso') else [] - ) - ) if self.param('sso') is not None else None, - quota=otypes.Quota(id=self._module.params.get('quota_id')) if self.param('quota_id') is not None else None, - high_availability=otypes.HighAvailability( - enabled=self.param('high_availability'), - priority=self.param('high_availability_priority'), - ) if self.param('high_availability') is not None or self.param('high_availability_priority') else None, - lease=otypes.StorageDomainLease( - storage_domain=otypes.StorageDomain( - id=get_id_by_name( - service=self._connection.system_service().storage_domains_service(), - name=self.param('lease') - ) - ) - ) if self.param('lease') is not None else None, - cpu=otypes.Cpu( - topology=otypes.CpuTopology( - cores=self.param('cpu_cores'), - sockets=self.param('cpu_sockets'), - threads=self.param('cpu_threads'), - ) if any(( - self.param('cpu_cores'), - self.param('cpu_sockets'), - self.param('cpu_threads') - )) else None, - cpu_tune=otypes.CpuTune( - vcpu_pins=[ - otypes.VcpuPin(vcpu=int(pin['vcpu']), cpu_set=str(pin['cpu'])) for pin in self.param('cpu_pinning') - ], - ) if self.param('cpu_pinning') else None, - mode=otypes.CpuMode(self.param('cpu_mode')) if self.param('cpu_mode') else None, - ) if any(( - self.param('cpu_cores'), - self.param('cpu_sockets'), - self.param('cpu_threads'), - self.param('cpu_mode'), - self.param('cpu_pinning') - )) else None, - cpu_shares=self.param('cpu_shares'), - os=otypes.OperatingSystem( - type=self.param('operating_system'), - boot=otypes.Boot( - devices=[ - otypes.BootDevice(dev) for dev in self.param('boot_devices') - ], - ) if self.param('boot_devices') else None, - cmdline=self.param('kernel_params') if self.param('kernel_params_persist') else None, - initrd=self.param('initrd_path') if self.param('kernel_params_persist') else None, - kernel=self.param('kernel_path') if self.param('kernel_params_persist') else None, - ) if ( - self.param('operating_system') or self.param('boot_devices') or self.param('kernel_params_persist') - ) else None, - type=otypes.VmType( - self.param('type') - ) if self.param('type') else None, - memory=convert_to_bytes( - self.param('memory') - ) if self.param('memory') else None, - memory_policy=otypes.MemoryPolicy( - guaranteed=convert_to_bytes(self.param('memory_guaranteed')), - ballooning=self.param('ballooning_enabled'), - max=convert_to_bytes(self.param('memory_max')), - ) if any(( - self.param('memory_guaranteed'), - self.param('ballooning_enabled') is not None, - self.param('memory_max') - )) else None, - instance_type=otypes.InstanceType( - id=get_id_by_name( - self._connection.system_service().instance_types_service(), - self.param('instance_type'), - ), - ) if self.param('instance_type') else None, - custom_compatibility_version=otypes.Version( - major=self._get_major(self.param('custom_compatibility_version')), - minor=self._get_minor(self.param('custom_compatibility_version')), - ) if self.param('custom_compatibility_version') is not None else None, - description=self.param('description'), - comment=self.param('comment'), - time_zone=otypes.TimeZone( - name=self.param('timezone'), - ) if self.param('timezone') else None, - serial_number=otypes.SerialNumber( - policy=otypes.SerialNumberPolicy(self.param('serial_policy')), - value=self.param('serial_policy_value'), - ) if ( - self.param('serial_policy') is not None or - self.param('serial_policy_value') is not None - ) else None, - placement_policy=otypes.VmPlacementPolicy( - affinity=otypes.VmAffinity(self.param('placement_policy')), - hosts=[ - otypes.Host(name=self.param('host')), - ] if self.param('host') else None, - ) if self.param('placement_policy') else None, - soundcard_enabled=self.param('soundcard_enabled'), - display=otypes.Display( - smartcard_enabled=self.param('smartcard_enabled') - ) if self.param('smartcard_enabled') is not None else None, - io=otypes.Io( - threads=self.param('io_threads'), - ) if self.param('io_threads') is not None else None, - numa_tune_mode=otypes.NumaTuneMode( - self.param('numa_tune_mode') - ) if self.param('numa_tune_mode') else None, - rng_device=otypes.RngDevice( - source=otypes.RngSource(self.param('rng_device')), - ) if self.param('rng_device') else None, - custom_properties=[ - otypes.CustomProperty( - name=cp.get('name'), - regexp=cp.get('regexp'), - value=str(cp.get('value')), - ) for cp in self.param('custom_properties') if cp - ] if self.param('custom_properties') is not None else None, - initialization=self.get_initialization() if self.param('cloud_init_persist') else None, - ) - - def _get_export_domain_service(self): - provider_name = self._module.params['export_domain'] - export_sds_service = self._connection.system_service().storage_domains_service() - export_sd_id = get_id_by_name(export_sds_service, provider_name) - return export_sds_service.service(export_sd_id) - - def post_export_action(self, entity): - self._service = self._get_export_domain_service().vms_service() - - def update_check(self, entity): - res = self._update_check(entity) - if entity.next_run_configuration_exists: - res = res and self._update_check(self._service.service(entity.id).get(next_run=True)) - - return res - - def _update_check(self, entity): - def check_cpu_pinning(): - if self.param('cpu_pinning'): - current = [] - if entity.cpu.cpu_tune: - current = [(str(pin.cpu_set), int(pin.vcpu)) for pin in entity.cpu.cpu_tune.vcpu_pins] - passed = [(str(pin['cpu']), int(pin['vcpu'])) for pin in self.param('cpu_pinning')] - return sorted(current) == sorted(passed) - return True - - def check_custom_properties(): - if self.param('custom_properties'): - current = [] - if entity.custom_properties: - current = [(cp.name, cp.regexp, str(cp.value)) for cp in entity.custom_properties] - passed = [(cp.get('name'), cp.get('regexp'), str(cp.get('value'))) for cp in self.param('custom_properties') if cp] - return sorted(current) == sorted(passed) - return True - - def check_host(): - if self.param('host') is not None: - return self.param('host') in [self._connection.follow_link(host).name for host in getattr(entity.placement_policy, 'hosts', None) or []] - return True - - def check_custom_compatibility_version(): - if self.param('custom_compatibility_version') is not None: - return (self._get_minor(self.param('custom_compatibility_version')) == self._get_minor(entity.custom_compatibility_version) and - self._get_major(self.param('custom_compatibility_version')) == self._get_major(entity.custom_compatibility_version)) - return True - - cpu_mode = getattr(entity.cpu, 'mode') - vm_display = entity.display - return ( - check_cpu_pinning() and - check_custom_properties() and - check_host() and - check_custom_compatibility_version() and - not self.param('cloud_init_persist') and - not self.param('kernel_params_persist') and - equal(self.param('cluster'), get_link_name(self._connection, entity.cluster)) and equal(convert_to_bytes(self.param('memory')), entity.memory) and - equal(convert_to_bytes(self.param('memory_guaranteed')), entity.memory_policy.guaranteed) and - equal(convert_to_bytes(self.param('memory_max')), entity.memory_policy.max) and - equal(self.param('cpu_cores'), entity.cpu.topology.cores) and - equal(self.param('cpu_sockets'), entity.cpu.topology.sockets) and - equal(self.param('cpu_threads'), entity.cpu.topology.threads) and - equal(self.param('cpu_mode'), str(cpu_mode) if cpu_mode else None) and - equal(self.param('type'), str(entity.type)) and - equal(self.param('name'), str(entity.name)) and - equal(self.param('operating_system'), str(entity.os.type)) and - equal(self.param('boot_menu'), entity.bios.boot_menu.enabled) and - equal(self.param('soundcard_enabled'), entity.soundcard_enabled) and - equal(self.param('smartcard_enabled'), getattr(vm_display, 'smartcard_enabled', False)) and - equal(self.param('io_threads'), entity.io.threads) and - equal(self.param('ballooning_enabled'), entity.memory_policy.ballooning) and - equal(self.param('serial_console'), getattr(entity.console, 'enabled', None)) and - equal(self.param('usb_support'), entity.usb.enabled) and - equal(self.param('sso'), True if entity.sso.methods else False) and - equal(self.param('quota_id'), getattr(entity.quota, 'id', None)) and - equal(self.param('high_availability'), entity.high_availability.enabled) and - equal(self.param('high_availability_priority'), entity.high_availability.priority) and - equal(self.param('lease'), get_link_name(self._connection, getattr(entity.lease, 'storage_domain', None))) and - equal(self.param('stateless'), entity.stateless) and - equal(self.param('cpu_shares'), entity.cpu_shares) and - equal(self.param('delete_protected'), entity.delete_protected) and - equal(self.param('use_latest_template_version'), entity.use_latest_template_version) and - equal(self.param('boot_devices'), [str(dev) for dev in getattr(entity.os.boot, 'devices', [])]) and - equal(self.param('instance_type'), get_link_name(self._connection, entity.instance_type), ignore_case=True) and - equal(self.param('description'), entity.description) and - equal(self.param('comment'), entity.comment) and - equal(self.param('timezone'), getattr(entity.time_zone, 'name', None)) and - equal(self.param('serial_policy'), str(getattr(entity.serial_number, 'policy', None))) and - equal(self.param('serial_policy_value'), getattr(entity.serial_number, 'value', None)) and - equal(self.param('placement_policy'), str(entity.placement_policy.affinity) if entity.placement_policy else None) and - equal(self.param('numa_tune_mode'), str(entity.numa_tune_mode)) and - equal(self.param('rng_device'), str(entity.rng_device.source) if entity.rng_device else None) - ) - - def pre_create(self, entity): - # Mark if entity exists before touching it: - if entity is None: - self._is_new = True - - def post_update(self, entity): - self.post_present(entity.id) - - def post_present(self, entity_id): - # After creation of the VM, attach disks and NICs: - entity = self._service.service(entity_id).get() - self.__attach_disks(entity) - self.__attach_nics(entity) - self._attach_cd(entity) - self.changed = self.__attach_numa_nodes(entity) - self.changed = self.__attach_watchdog(entity) - self.changed = self.__attach_graphical_console(entity) - self.changed = self.__attach_host_devices(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.param('cd_iso') - if cd_iso is not None: - vm_service = self._service.service(entity.id) - current = vm_service.get().status == otypes.VmStatus.UP and self.param('state') == 'running' - 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.param('host') - vm_service = self._service.vm_service(entity.id) - # In case VM is preparing to be UP, wait to be up, to migrate it: - if entity.status == otypes.VmStatus.UP: - if vm_host is not None: - 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), force=self.param('force_migrate')) - self._wait_for_UP(vm_service) - self.changed = True - elif self.param('migrate'): - if not self._module.check_mode: - vm_service.migrate(force=self.param('force_migrate')) - 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.param('wait'), - timeout=self.param('timeout'), - ) - - def _wait_for_vm_disks(self, vm_service): - disks_service = self._connection.system_service().disks_service() - for da in vm_service.disk_attachments_service().list(): - disk_service = disks_service.disk_service(da.disk.id) - wait( - service=disk_service, - condition=lambda disk: disk.status == otypes.DiskStatus.OK if disk.storage_type == otypes.DiskStorageType.IMAGE else True, - wait=self.param('wait'), - timeout=self.param('timeout'), - ) - - def wait_for_down(self, vm): - """ - This function will first wait for the status DOWN of the VM. - Then it will find the active snapshot and wait until it's state is OK for - stateless VMs and statless snaphot is removed. - """ - vm_service = self._service.vm_service(vm.id) - wait( - service=vm_service, - condition=lambda vm: vm.status == otypes.VmStatus.DOWN, - wait=self.param('wait'), - timeout=self.param('timeout'), - ) - if vm.stateless: - snapshots_service = vm_service.snapshots_service() - snapshots = snapshots_service.list() - snap_active = [ - snap for snap in snapshots - if snap.snapshot_type == otypes.SnapshotType.ACTIVE - ][0] - snap_stateless = [ - snap for snap in snapshots - if snap.snapshot_type == otypes.SnapshotType.STATELESS - ] - # Stateless snapshot may be already removed: - if snap_stateless: - """ - We need to wait for Active snapshot ID, to be removed as it's current - stateless snapshot. Then we need to wait for staless snapshot ID to - be read, for use, because it will become active snapshot. - """ - wait( - service=snapshots_service.snapshot_service(snap_active.id), - condition=lambda snap: snap is None, - wait=self.param('wait'), - timeout=self.param('timeout'), - ) - wait( - service=snapshots_service.snapshot_service(snap_stateless[0].id), - condition=lambda snap: snap.snapshot_status == otypes.SnapshotStatus.OK, - wait=self.param('wait'), - timeout=self.param('timeout'), - ) - return True - - def __attach_graphical_console(self, entity): - graphical_console = self.param('graphical_console') - if not graphical_console: - return False - - vm_service = self._service.service(entity.id) - gcs_service = vm_service.graphics_consoles_service() - graphical_consoles = gcs_service.list() - - # Remove all graphical consoles if there are any: - if bool(graphical_console.get('headless_mode')): - if not self._module.check_mode: - for gc in graphical_consoles: - gcs_service.console_service(gc.id).remove() - return len(graphical_consoles) > 0 - - # If there are not gc add any gc to be added: - protocol = graphical_console.get('protocol') - if isinstance(protocol, str): - protocol = [protocol] - - current_protocols = [str(gc.protocol) for gc in graphical_consoles] - if not current_protocols: - if not self._module.check_mode: - for p in protocol: - gcs_service.add( - otypes.GraphicsConsole( - protocol=otypes.GraphicsType(p), - ) - ) - return True - - # Update consoles: - if sorted(protocol) != sorted(current_protocols): - if not self._module.check_mode: - for gc in graphical_consoles: - gcs_service.console_service(gc.id).remove() - for p in protocol: - gcs_service.add( - otypes.GraphicsConsole( - protocol=otypes.GraphicsType(p), - ) - ) - return True - - def __attach_disks(self, entity): - if not self.param('disks'): - return - - vm_service = self._service.service(entity.id) - disks_service = self._connection.system_service().disks_service() - disk_attachments_service = vm_service.disk_attachments_service() - - self._wait_for_vm_disks(vm_service) - for disk in self.param('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_attachment = disk_attachments_service.attachment_service(disk_id) - if get_entity(disk_attachment) 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 __get_vnic_profile_id(self, nic): - """ - Return VNIC profile ID looked up by it's name, because there can be - more VNIC profiles with same name, other criteria of filter is cluster. - """ - vnics_service = self._connection.system_service().vnic_profiles_service() - clusters_service = self._connection.system_service().clusters_service() - cluster = search_by_name(clusters_service, self.param('cluster')) - profiles = [ - profile for profile in vnics_service.list() - if profile.name == nic.get('profile_name') - ] - cluster_networks = [ - net.id for net in self._connection.follow_link(cluster.networks) - ] - try: - return next( - profile.id for profile in profiles - if profile.network.id in cluster_networks - ) - except StopIteration: - raise Exception( - "Profile '%s' was not found in cluster '%s'" % ( - nic.get('profile_name'), - self.param('cluster') - ) - ) - - def __attach_numa_nodes(self, entity): - updated = False - numa_nodes_service = self._service.service(entity.id).numa_nodes_service() - - if len(self.param('numa_nodes')) > 0: - # Remove all existing virtual numa nodes before adding new ones - existed_numa_nodes = numa_nodes_service.list() - existed_numa_nodes.sort(reverse=len(existed_numa_nodes) > 1 and existed_numa_nodes[1].index > existed_numa_nodes[0].index) - for current_numa_node in existed_numa_nodes: - numa_nodes_service.node_service(current_numa_node.id).remove() - updated = True - - for numa_node in self.param('numa_nodes'): - if numa_node is None or numa_node.get('index') is None or numa_node.get('cores') is None or numa_node.get('memory') is None: - continue - - numa_nodes_service.add( - otypes.VirtualNumaNode( - index=numa_node.get('index'), - memory=numa_node.get('memory'), - cpu=otypes.Cpu( - cores=[ - otypes.Core( - index=core - ) for core in numa_node.get('cores') - ], - ), - numa_node_pins=[ - otypes.NumaNodePin( - index=pin - ) for pin in numa_node.get('numa_node_pins') - ] if numa_node.get('numa_node_pins') is not None else None, - ) - ) - updated = True - - return updated - - def __attach_watchdog(self, entity): - watchdogs_service = self._service.service(entity.id).watchdogs_service() - watchdog = self.param('watchdog') - if watchdog is not None: - current_watchdog = next(iter(watchdogs_service.list()), None) - if watchdog.get('model') is None and current_watchdog: - watchdogs_service.watchdog_service(current_watchdog.id).remove() - return True - elif watchdog.get('model') is not None and current_watchdog is None: - watchdogs_service.add( - otypes.Watchdog( - model=otypes.WatchdogModel(watchdog.get('model').lower()), - action=otypes.WatchdogAction(watchdog.get('action')), - ) - ) - return True - elif current_watchdog is not None: - if ( - str(current_watchdog.model).lower() != watchdog.get('model').lower() or - str(current_watchdog.action).lower() != watchdog.get('action').lower() - ): - watchdogs_service.watchdog_service(current_watchdog.id).update( - otypes.Watchdog( - model=otypes.WatchdogModel(watchdog.get('model')), - action=otypes.WatchdogAction(watchdog.get('action')), - ) - ) - return True - return False - - def __attach_nics(self, entity): - # Attach NICs to VM, if specified: - nics_service = self._service.service(entity.id).nics_service() - for nic in self.param('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=self.__get_vnic_profile_id(nic), - ) 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(self): - if self._initialization is not None: - return self._initialization - - sysprep = self.param('sysprep') - cloud_init = self.param('cloud_init') - cloud_init_nics = self.param('cloud_init_nics') or [] - if cloud_init is not None: - cloud_init_nics.append(cloud_init) - - if cloud_init or cloud_init_nics: - self._initialization = otypes.Initialization( - nic_configurations=[ - otypes.NicConfiguration( - boot_protocol=otypes.BootProtocol( - nic.pop('nic_boot_protocol').lower() - ) if nic.get('nic_boot_protocol') else None, - name=nic.pop('nic_name', None), - on_boot=nic.pop('nic_on_boot', None), - ip=otypes.Ip( - address=nic.pop('nic_ip_address', None), - netmask=nic.pop('nic_netmask', None), - gateway=nic.pop('nic_gateway', None), - ) if ( - nic.get('nic_gateway') is not None or - nic.get('nic_netmask') is not None or - nic.get('nic_ip_address') is not None - ) else None, - ) - for nic in cloud_init_nics - if ( - nic.get('nic_gateway') is not None or - nic.get('nic_netmask') is not None or - nic.get('nic_ip_address') is not None or - nic.get('nic_boot_protocol') is not None or - nic.get('nic_on_boot') is not None - ) - ] if cloud_init_nics else None, - **cloud_init - ) - elif sysprep: - self._initialization = otypes.Initialization( - **sysprep - ) - return self._initialization - - def __attach_host_devices(self, entity): - vm_service = self._service.service(entity.id) - host_devices_service = vm_service.host_devices_service() - host_devices = self.param('host_devices') - updated = False - if host_devices: - device_names = [dev.name for dev in host_devices_service.list()] - for device in host_devices: - device_name = device.get('name') - state = device.get('state', 'present') - if state == 'absent' and device_name in device_names: - updated = True - if not self._module.check_mode: - device_id = get_id_by_name(host_devices_service, device.get('name')) - host_devices_service.device_service(device_id).remove() - - elif state == 'present' and device_name not in device_names: - updated = True - if not self._module.check_mode: - host_devices_service.add( - otypes.HostDevice( - name=device.get('name'), - ) - ) - - return updated - - -def _get_role_mappings(module): - roleMappings = list() - for roleMapping in module.params['role_mappings']: - roleMappings.append( - otypes.RegistrationRoleMapping( - from_=otypes.Role( - name=roleMapping['source_name'], - ) if roleMapping['source_name'] else None, - to=otypes.Role( - name=roleMapping['dest_name'], - ) if roleMapping['dest_name'] else None, - ) - ) - return roleMappings - - -def _get_affinity_group_mappings(module): - affinityGroupMappings = list() - - for affinityGroupMapping in module.params['affinity_group_mappings']: - affinityGroupMappings.append( - otypes.RegistrationAffinityGroupMapping( - from_=otypes.AffinityGroup( - name=affinityGroupMapping['source_name'], - ) if affinityGroupMapping['source_name'] else None, - to=otypes.AffinityGroup( - name=affinityGroupMapping['dest_name'], - ) if affinityGroupMapping['dest_name'] else None, - ) - ) - return affinityGroupMappings - - -def _get_affinity_label_mappings(module): - affinityLabelMappings = list() - - for affinityLabelMapping in module.params['affinity_label_mappings']: - affinityLabelMappings.append( - otypes.RegistrationAffinityLabelMapping( - from_=otypes.AffinityLabel( - name=affinityLabelMapping['source_name'], - ) if affinityLabelMapping['source_name'] else None, - to=otypes.AffinityLabel( - name=affinityLabelMapping['dest_name'], - ) if affinityLabelMapping['dest_name'] else None, - ) - ) - return affinityLabelMappings - - -def _get_domain_mappings(module): - domainMappings = list() - - for domainMapping in module.params['domain_mappings']: - domainMappings.append( - otypes.RegistrationDomainMapping( - from_=otypes.Domain( - name=domainMapping['source_name'], - ) if domainMapping['source_name'] else None, - to=otypes.Domain( - name=domainMapping['dest_name'], - ) if domainMapping['dest_name'] else None, - ) - ) - return domainMappings - - -def _get_lun_mappings(module): - lunMappings = list() - for lunMapping in module.params['lun_mappings']: - lunMappings.append( - otypes.RegistrationLunMapping( - from_=otypes.Disk( - lun_storage=otypes.HostStorage( - type=otypes.StorageType(lunMapping['source_storage_type']) - if (lunMapping['source_storage_type'] in - ['iscsi', 'fcp']) else None, - logical_units=[ - otypes.LogicalUnit( - id=lunMapping['source_logical_unit_id'], - ) - ], - ), - ) if lunMapping['source_logical_unit_id'] else None, - to=otypes.Disk( - lun_storage=otypes.HostStorage( - type=otypes.StorageType(lunMapping['dest_storage_type']) - if (lunMapping['dest_storage_type'] in - ['iscsi', 'fcp']) else None, - logical_units=[ - otypes.LogicalUnit( - id=lunMapping['dest_logical_unit_id'], - port=lunMapping['dest_logical_unit_port'], - portal=lunMapping['dest_logical_unit_portal'], - address=lunMapping['dest_logical_unit_address'], - target=lunMapping['dest_logical_unit_target'], - password=lunMapping['dest_logical_unit_password'], - username=lunMapping['dest_logical_unit_username'], - ) - ], - ), - ) if lunMapping['dest_logical_unit_id'] else None, - ), - ), - return lunMappings - - -def _get_cluster_mappings(module): - clusterMappings = list() - - for clusterMapping in module.params['cluster_mappings']: - clusterMappings.append( - otypes.RegistrationClusterMapping( - from_=otypes.Cluster( - name=clusterMapping['source_name'], - ), - to=otypes.Cluster( - name=clusterMapping['dest_name'], - ) if clusterMapping['dest_name'] else None, - ) - ) - return clusterMappings - - -def _get_vnic_profile_mappings(module): - vnicProfileMappings = list() - - for vnicProfileMapping in module.params['vnic_profile_mappings']: - vnicProfileMappings.append( - otypes.VnicProfileMapping( - source_network_name=vnicProfileMapping['source_network_name'], - source_network_profile_name=vnicProfileMapping['source_profile_name'], - target_vnic_profile=otypes.VnicProfile( - id=vnicProfileMapping['target_profile_id'], - ) if vnicProfileMapping['target_profile_id'] else None, - ) - ) - - return vnicProfileMappings - - -def import_vm(module, connection): - vms_service = connection.system_service().vms_service() - if search_by_name(vms_service, module.params['name']) is not None: - return False - - events_service = connection.system_service().events_service() - last_event = events_service.list(max=1)[0] - - external_type = [ - tmp for tmp in ['kvm', 'xen', 'vmware'] - if module.params[tmp] is not None - ][0] - - external_vm = module.params[external_type] - imports_service = connection.system_service().external_vm_imports_service() - imported_vm = imports_service.add( - otypes.ExternalVmImport( - vm=otypes.Vm( - name=module.params['name'] - ), - name=external_vm.get('name'), - username=external_vm.get('username', 'test'), - password=external_vm.get('password', 'test'), - provider=otypes.ExternalVmProviderType(external_type), - url=external_vm.get('url'), - cluster=otypes.Cluster( - name=module.params['cluster'], - ) if module.params['cluster'] else None, - storage_domain=otypes.StorageDomain( - name=external_vm.get('storage_domain'), - ) if external_vm.get('storage_domain') else None, - sparse=external_vm.get('sparse', True), - host=otypes.Host( - name=module.params['host'], - ) if module.params['host'] else None, - ) - ) - - # Wait until event with code 1152 for our VM don't appear: - vms_service = connection.system_service().vms_service() - wait( - service=vms_service.vm_service(imported_vm.vm.id), - condition=lambda vm: len([ - event - for event in events_service.list( - from_=int(last_event.id), - search='type=1152 and vm.id=%s' % vm.id, - ) - ]) > 0 if vm is not None else False, - fail_condition=lambda vm: vm is None, - timeout=module.params['timeout'], - poll_interval=module.params['poll_interval'], - ) - return True - - -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(msg="Not possible to control VM, if it's in '{0}' 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(type='str', default='present', choices=['absent', 'next_run', 'present', 'registered', 'running', 'stopped', 'suspended', 'exported']), - name=dict(type='str'), - id=dict(type='str'), - cluster=dict(type='str'), - allow_partial_import=dict(type='bool'), - template=dict(type='str'), - template_version=dict(type='int'), - use_latest_template_version=dict(type='bool'), - storage_domain=dict(type='str'), - disk_format=dict(type='str', default='cow', choices=['cow', 'raw']), - disks=dict(type='list', default=[]), - memory=dict(type='str'), - memory_guaranteed=dict(type='str'), - memory_max=dict(type='str'), - cpu_sockets=dict(type='int'), - cpu_cores=dict(type='int'), - cpu_shares=dict(type='int'), - cpu_threads=dict(type='int'), - type=dict(type='str', choices=['server', 'desktop', 'high_performance']), - operating_system=dict(type='str'), - cd_iso=dict(type='str'), - boot_devices=dict(type='list', choices=['cdrom', 'hd', 'network']), - vnic_profile_mappings=dict(default=[], type='list'), - cluster_mappings=dict(default=[], type='list'), - role_mappings=dict(default=[], type='list'), - affinity_group_mappings=dict(default=[], type='list'), - affinity_label_mappings=dict(default=[], type='list'), - lun_mappings=dict(default=[], type='list'), - domain_mappings=dict(default=[], type='list'), - reassign_bad_macs=dict(default=None, type='bool'), - boot_menu=dict(type='bool'), - serial_console=dict(type='bool'), - usb_support=dict(type='bool'), - sso=dict(type='bool'), - quota_id=dict(type='str'), - high_availability=dict(type='bool'), - high_availability_priority=dict(type='int'), - lease=dict(type='str'), - stateless=dict(type='bool'), - delete_protected=dict(type='bool'), - force=dict(type='bool', default=False), - nics=dict(type='list', default=[]), - cloud_init=dict(type='dict'), - cloud_init_nics=dict(type='list', default=[]), - cloud_init_persist=dict(type='bool', default=False, aliases=['sysprep_persist']), - kernel_params_persist=dict(type='bool', default=False), - sysprep=dict(type='dict'), - host=dict(type='str'), - clone=dict(type='bool', default=False), - clone_permissions=dict(type='bool', default=False), - kernel_path=dict(type='str'), - initrd_path=dict(type='str'), - kernel_params=dict(type='str'), - instance_type=dict(type='str'), - description=dict(type='str'), - comment=dict(type='str'), - timezone=dict(type='str'), - serial_policy=dict(type='str', choices=['vm', 'host', 'custom']), - serial_policy_value=dict(type='str'), - vmware=dict(type='dict'), - xen=dict(type='dict'), - kvm=dict(type='dict'), - cpu_mode=dict(type='str'), - placement_policy=dict(type='str'), - custom_compatibility_version=dict(type='str'), - ticket=dict(type='bool', default=None), - cpu_pinning=dict(type='list'), - soundcard_enabled=dict(type='bool', default=None), - smartcard_enabled=dict(type='bool', default=None), - io_threads=dict(type='int', default=None), - ballooning_enabled=dict(type='bool', default=None), - rng_device=dict(type='str'), - numa_tune_mode=dict(type='str', choices=['interleave', 'preferred', 'strict']), - numa_nodes=dict(type='list', default=[]), - custom_properties=dict(type='list'), - watchdog=dict(type='dict'), - host_devices=dict(type='list'), - graphical_console=dict(type='dict'), - exclusive=dict(type='bool'), - export_domain=dict(default=None), - export_ova=dict(type='dict'), - force_migrate=dict(type='bool'), - migrate=dict(type='bool', default=None), - next_run=dict(type='bool'), - ) - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_one_of=[['id', 'name']], - required_if=[ - ('state', 'registered', ['storage_domain']), - ] - ) - - check_sdk(module) - check_params(module) - - try: - state = module.params['state'] - auth = module.params.pop('auth') - connection = create_connection(auth) - vms_service = connection.system_service().vms_service() - vms_module = VmsModule( - connection=connection, - module=module, - service=vms_service, - ) - vm = vms_module.search_entity(list_params={'all_content': True}) - - control_state(vm, vms_service, module) - if state in ('present', 'running', 'next_run'): - if module.params['xen'] or module.params['kvm'] or module.params['vmware']: - vms_module.changed = import_vm(module, connection) - - # In case of wait=false and state=running, waits for VM to be created - # In case VM don't exist, wait for VM DOWN state, - # otherwise don't wait for any state, just update VM: - ret = vms_module.create( - entity=vm, - result_state=otypes.VmStatus.DOWN if vm is None else None, - update_params={'next_run': module.params['next_run']} if module.params['next_run'] is not None else None, - clone=module.params['clone'], - clone_permissions=module.params['clone_permissions'], - _wait=True if not module.params['wait'] and state == 'running' else module.params['wait'], - ) - # If VM is going to be created and check_mode is on, return now: - if module.check_mode and ret.get('id') is None: - module.exit_json(**ret) - - vms_module.post_present(ret['id']) - # Run the VM if it was just created, else don't run it: - if state == 'running': - def kernel_persist_check(): - return (module.params.get('kernel_params') or - module.params.get('initrd_path') or - module.params.get('kernel_path') - and not module.params.get('cloud_init_persist')) - initialization = vms_module.get_initialization() - 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=True if not module.params.get('cloud_init_persist') and module.params.get('cloud_init') is not None else None, - use_sysprep=True if not module.params.get('cloud_init_persist') and module.params.get('sysprep') is not None else None, - vm=otypes.Vm( - placement_policy=otypes.VmPlacementPolicy( - hosts=[otypes.Host(name=module.params['host'])] - ) if module.params['host'] else None, - initialization=initialization, - os=otypes.OperatingSystem( - cmdline=module.params.get('kernel_params'), - initrd=module.params.get('initrd_path'), - kernel=module.params.get('kernel_path'), - ) if (kernel_persist_check()) else None, - ) if ( - kernel_persist_check() or - module.params.get('host') or - initialization is not None - and not module.params.get('cloud_init_persist') - ) else None, - ) - - if module.params['ticket']: - vm_service = vms_service.vm_service(ret['id']) - graphics_consoles_service = vm_service.graphics_consoles_service() - graphics_console = graphics_consoles_service.list()[0] - console_service = graphics_consoles_service.console_service(graphics_console.id) - ticket = console_service.remote_viewer_connection_file() - if ticket: - ret['vm']['remote_vv_file'] = ticket - - 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, - ) - ret['changed'] = vms_module.changed - elif state == 'stopped': - if module.params['xen'] or module.params['kvm'] or module.params['vmware']: - vms_module.changed = import_vm(module, connection) - - ret = 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'], - ) - if module.params['force']: - ret = vms_module.action( - action='stop', - action_condition=lambda vm: vm.status != otypes.VmStatus.DOWN, - wait_condition=vms_module.wait_for_down, - ) - else: - ret = vms_module.action( - action='shutdown', - pre_action=vms_module._pre_shutdown_action, - action_condition=lambda vm: vm.status != otypes.VmStatus.DOWN, - wait_condition=vms_module.wait_for_down, - ) - vms_module.post_present(ret['id']) - elif state == 'suspended': - ret = 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'], - ) - vms_module.post_present(ret['id']) - 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() - elif state == 'registered': - storage_domains_service = connection.system_service().storage_domains_service() - - # Find the storage domain with unregistered VM: - sd_id = get_id_by_name(storage_domains_service, module.params['storage_domain']) - storage_domain_service = storage_domains_service.storage_domain_service(sd_id) - vms_service = storage_domain_service.vms_service() - - # Find the unregistered VM we want to register: - vms = vms_service.list(unregistered=True) - vm = next( - (vm for vm in vms if (vm.id == module.params['id'] or vm.name == module.params['name'])), - None - ) - changed = False - if vm is None: - vm = vms_module.search_entity() - if vm is None: - raise ValueError( - "VM '%s(%s)' wasn't found." % (module.params['name'], module.params['id']) - ) - else: - # Register the vm into the system: - changed = True - vm_service = vms_service.vm_service(vm.id) - vm_service.register( - allow_partial_import=module.params['allow_partial_import'], - cluster=otypes.Cluster( - name=module.params['cluster'] - ) if module.params['cluster'] else None, - vnic_profile_mappings=_get_vnic_profile_mappings(module) - if module.params['vnic_profile_mappings'] else None, - reassign_bad_macs=module.params['reassign_bad_macs'] - if module.params['reassign_bad_macs'] is not None else None, - registration_configuration=otypes.RegistrationConfiguration( - cluster_mappings=_get_cluster_mappings(module), - role_mappings=_get_role_mappings(module), - domain_mappings=_get_domain_mappings(module), - lun_mappings=_get_lun_mappings(module), - affinity_group_mappings=_get_affinity_group_mappings(module), - affinity_label_mappings=_get_affinity_label_mappings(module), - ) if (module.params['cluster_mappings'] - or module.params['role_mappings'] - or module.params['domain_mappings'] - or module.params['lun_mappings'] - or module.params['affinity_group_mappings'] - or module.params['affinity_label_mappings']) else None - ) - - if module.params['wait']: - vm = vms_module.wait_for_import() - else: - # Fetch vm to initialize return. - vm = vm_service.get() - ret = { - 'changed': changed, - 'id': vm.id, - 'vm': get_dict_of_struct(vm) - } - elif state == 'exported': - if module.params['export_domain']: - export_service = vms_module._get_export_domain_service() - export_vm = search_by_attributes(export_service.vms_service(), id=vm.id) - - ret = vms_module.action( - entity=vm, - action='export', - action_condition=lambda t: export_vm is None or module.params['exclusive'], - wait_condition=lambda t: t is not None, - post_action=vms_module.post_export_action, - storage_domain=otypes.StorageDomain(id=export_service.get().id), - exclusive=module.params['exclusive'], - ) - elif module.params['export_ova']: - export_vm = module.params['export_ova'] - ret = vms_module.action( - entity=vm, - action='export_to_path_on_host', - host=otypes.Host(name=export_vm.get('host')), - directory=export_vm.get('directory'), - filename=export_vm.get('filename'), - ) - - module.exit_json(**ret) - except Exception as e: - module.fail_json(msg=str(e), exception=traceback.format_exc()) - finally: - connection.close(logout=auth.get('token') is None) - - -if __name__ == "__main__": - main() diff --git a/meta/main.yml b/meta/main.yml index 7ed7cff..742620a 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -5,7 +5,7 @@ galaxy_info: license: Apache License 2.0 - min_ansible_version: 2.5 + min_ansible_version: 2.9 platforms: - name: EL diff --git a/ovirt-ansible-cluster-upgrade.spec.in b/ovirt-ansible-cluster-upgrade.spec.in index 0bb07d6..17b41ea 100644 --- a/ovirt-ansible-cluster-upgrade.spec.in +++ b/ovirt-ansible-cluster-upgrade.spec.in @@ -14,7 +14,7 @@ Group: Virtualization/Management BuildArch: noarch Url: http://www.ovirt.org -Requires: ansible >= 2.7.2 +Requires: ansible >= 2.9.0 %description This Ansible role provide funtionality to upgrade cluster of hosts in oVirt engine, diff --git a/tasks/main.yml b/tasks/main.yml index 3390b2e..077b215 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -47,7 +47,7 @@ when: ovirt_api.product_info.version.major >= 4 and ovirt_api.product_info.version.minor >= 3 - name: Log event cluster upgrade has started - ovirt_event_28: + ovirt_event: auth: "{{ ovirt_auth }}" state: present description: "Cluster upgrade started for {{ cluster_name }}." @@ -68,7 +68,7 @@ msg: "No hosts to be updated" - name: Log event - no hosts to be updated - ovirt_event_28: + ovirt_event: auth: "{{ ovirt_auth }}" state: present description: "There are no hosts to be updated for cluster {{ cluster_name }}." @@ -80,7 +80,7 @@ - block: - name: Log event about hosts that are marked to be updated - ovirt_event_28: + ovirt_event: auth: "{{ ovirt_auth }}" state: present description: "Hosts {{ ovirt_hosts | map(attribute='name') | join(',') }} are marked to be updated in cluster {{ cluster_name }}." @@ -101,7 +101,7 @@ - include_tasks: pinned_vms.yml - name: Start ovirt job session - ovirt_job_29: + ovirt_job: auth: "{{ ovirt_auth }}" description: "Upgrading hosts" @@ -113,13 +113,13 @@ when: "item.id not in host_ids or stop_non_migratable_vms" - name: Start ovirt job session - ovirt_job_29: + ovirt_job: auth: "{{ ovirt_auth }}" description: "Upgrading hosts" state: finished - name: Log event about cluster upgrade finished successfully - ovirt_event_28: + ovirt_event: auth: "{{ ovirt_auth }}" state: present description: "Upgrade of cluster {{ cluster_name }} finished successfully." @@ -131,7 +131,7 @@ when: ovirt_hosts | length > 0 rescue: - name: Log event about cluster upgrade failed - ovirt_event_28: + ovirt_event: auth: "{{ ovirt_auth }}" state: present description: "Upgrade of cluster {{ cluster_name }} failed." diff --git a/tasks/upgrade.yml b/tasks/upgrade.yml index c62594e..8626ea4 100644 --- a/tasks/upgrade.yml +++ b/tasks/upgrade.yml @@ -5,7 +5,7 @@ check_mode: "no" - name: Move user migratable vms - ovirt_vm_28: + ovirt_vm: auth: "{{ ovirt_auth }}" force_migrate: true migrate: true @@ -38,7 +38,7 @@ pinned_vms_names: "{{ pinned_vms_names + pinned_to_host_vms.results | selectattr('changed') | map(attribute='item.name') | list }}" - name: Start ovirt job step - ovirt_job_29: + ovirt_job: auth: "{{ ovirt_auth }}" description: "Upgrading hosts" steps: @@ -46,7 +46,7 @@ - name: Upgrade host # TODO: change to ovirt_host, when Ansible 2.8 is out: - ovirt_host_28: + ovirt_host: auth: "{{ ovirt_auth }}" name: "{{ item.name }}" state: upgraded @@ -55,7 +55,7 @@ timeout: "{{ upgrade_timeout }}" - name: Finish ovirt job step - ovirt_job_29: + ovirt_job: auth: "{{ ovirt_auth }}" description: "Upgrading hosts" steps: diff --git a/tests/requirements.txt b/tests/requirements.txt index ea8a68f..f3667c9 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,2 +1,2 @@ -ansible +ansible>=2.9.0 ansible-lint From 203aa4ddfae175ce0b01335bb066d296b4171be5 Mon Sep 17 00:00:00 2001 From: mnecas Date: Sun, 10 Nov 2019 23:34:30 +0100 Subject: [PATCH 2/4] change ovirt facts to info --- tasks/cluster_policy.yml | 14 ++++---------- tasks/main.yml | 41 +++++++++++++++++++++------------------- tasks/upgrade.yml | 3 +-- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/tasks/cluster_policy.yml b/tasks/cluster_policy.yml index 615b373..077a64e 100644 --- a/tasks/cluster_policy.yml +++ b/tasks/cluster_policy.yml @@ -1,13 +1,7 @@ -- name: Get cluster facts - ovirt_cluster_facts: - auth: "{{ ovirt_auth }}" - pattern: "name={{ cluster_name }}" - check_mode: "no" - - name: Get name of the original scheduling policy - ovirt_scheduling_policy_facts: + ovirt_scheduling_policy_info: auth: "{{ ovirt_auth }}" - id: "{{ ovirt_clusters[0].scheduling_policy.id }}" + id: "{{ cluster_info.ovirt_clusters[0].scheduling_policy.id }}" check_mode: "no" - name: Remember the cluster scheduling policy @@ -25,5 +19,5 @@ scheduling_policy: cluster_maintenance register: cluster_policy when: - - (ovirt_api.product_info.version.major >= 4 and ovirt_api.product_info.version.major >= 2) or - (ovirt_api.product_info.version.major == 4 and ovirt_api.product_info.version.major == 1 and ovirt_api.product_info.version.revision >= 4) + - (api_info.ovirt_api.product_info.version.major >= 4 and api_info.ovirt_api.product_info.version.major >= 2) or + (api_info.ovirt_api.product_info.version.major == 4 and api_info.ovirt_api.product_info.version.major == 1 and api_info.ovirt_api.product_info.version.revision >= 4) diff --git a/tasks/main.yml b/tasks/main.yml index b8be9b0..8da8ea9 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -22,21 +22,23 @@ tags: - always - - name: Get API facts - ovirt_api_facts: + - name: Get API info + ovirt_api_info: auth: "{{ ovirt_auth }}" + register: api_info check_mode: "no" - - name: Get cluster - ovirt_cluster_facts: + - name: Get cluster info + ovirt_cluster_info: auth: "{{ ovirt_auth }}" pattern: "name={{ cluster_name }}" check_mode: "no" + register: cluster_info - name: Set cluster upgrade status in progress no_log: true uri: - url: "{{ ovirt_auth.url }}/clusters/{{ ovirt_clusters[0].id }}/upgrade" + url: "{{ ovirt_auth.url }}/clusters/{{ cluster_info.ovirt_clusters[0].id }}/upgrade" method: POST body_format: json validate_certs: false @@ -45,7 +47,7 @@ body: upgrade_action: start register: upgrade_set - when: ovirt_api.product_info.version.major >= 4 and ovirt_api.product_info.version.minor >= 3 + when: api_info.ovirt_api.product_info.version.major >= 4 and api_info.ovirt_api.product_info.version.minor >= 3 - name: Log event cluster upgrade has started ovirt_event: @@ -55,13 +57,14 @@ origin: "cluster_upgrade" custom_id: "{{ 2147483647 | random | int }}" severity: normal - cluster: "{{ ovirt_clusters[0].id }}" + cluster: "{{ cluster_info.ovirt_clusters[0].id }}" - name: Get hosts - ovirt_host_facts: + ovirt_host_info: auth: "{{ ovirt_auth }}" pattern: "cluster={{ cluster_name | mandatory }} {{ check_upgrade | ternary('', 'update_available=true') }} {{ host_names | map('regex_replace', '^(.*)$', 'name=\\1') | list | join(' or ') }} {{ host_statuses | map('regex_replace', '^(.*)$', 'status=\\1') | list | join(' or ') }}" check_mode: "no" + register: host_info - block: - name: Print - no hosts to be updated @@ -76,25 +79,25 @@ origin: "cluster_upgrade" custom_id: "{{ 2147483647 | random | int }}" severity: normal - cluster: "{{ ovirt_clusters[0].id }}" - when: ovirt_hosts | length == 0 + cluster: "{{ cluster_info.ovirt_clusters[0].id }}" + when: host_info.ovirt_hosts | length == 0 - block: - name: Log event about hosts that are marked to be updated ovirt_event: auth: "{{ ovirt_auth }}" state: present - description: "Hosts {{ ovirt_hosts | map(attribute='name') | join(',') }} are marked to be updated in cluster {{ cluster_name }}." + description: "Hosts {{ host_info.ovirt_hosts | map(attribute='name') | join(',') }} are marked to be updated in cluster {{ cluster_name }}." origin: "cluster_upgrade" custom_id: "{{ 2147483647 | random | int }}" severity: normal - cluster: "{{ ovirt_clusters[0].id }}" + cluster: "{{ cluster_info.ovirt_clusters[0].id }}" - include_tasks: cluster_policy.yml when: use_maintenance_policy - name: Get list of VMs in cluster - ovirt_vm_facts: + ovirt_vm_info: auth: "{{ ovirt_auth }}" pattern: "cluster={{ cluster_name }}" check_mode: "no" @@ -110,7 +113,7 @@ # or if stop_non_migratable_vms is enabled, which means we stop pinned VMs - include_tasks: upgrade.yml with_items: - - "{{ ovirt_hosts }}" + - "{{ host_info.ovirt_hosts }}" when: "item.id not in host_ids or stop_non_migratable_vms" - name: Start ovirt job session @@ -127,9 +130,9 @@ origin: "cluster_upgrade" severity: normal custom_id: "{{ 2147483647 | random | int }}" - cluster: "{{ ovirt_clusters[0].id }}" + cluster: "{{ cluster_info.ovirt_clusters[0].id }}" - when: ovirt_hosts | length > 0 + when: host_info.ovirt_hosts | length > 0 rescue: - name: Log event about cluster upgrade failed ovirt_event: @@ -139,7 +142,7 @@ origin: "cluster_upgrade" custom_id: "{{ 2147483647 | random | int }}" severity: error - cluster: "{{ ovirt_clusters[0].id }}" + cluster: "{{ cluster_info.ovirt_clusters[0].id }}" always: - name: Set original cluster policy @@ -173,7 +176,7 @@ - name: Set cluster upgrade status to finished no_log: true uri: - url: "{{ ovirt_auth.url }}/clusters/{{ ovirt_clusters[0].id }}/upgrade" + url: "{{ ovirt_auth.url }}/clusters/{{ cluster_info.ovirt_clusters[0].id }}/upgrade" validate_certs: false method: POST body_format: json @@ -183,7 +186,7 @@ upgrade_action: finish when: - upgrade_set is defined and not upgrade_set.failed | default(false) - - ovirt_api.product_info.version.major >= 4 and ovirt_api.product_info.version.minor >= 3 + - api_info.ovirt_api.product_info.version.major >= 4 and api_info.ovirt_api.product_info.version.minor >= 3 - name: Logout from oVirt ovirt_auth: diff --git a/tasks/upgrade.yml b/tasks/upgrade.yml index 8626ea4..bcaa631 100644 --- a/tasks/upgrade.yml +++ b/tasks/upgrade.yml @@ -1,5 +1,5 @@ - name: Get list of VMs in host - ovirt_vm_facts: + ovirt_vm_info: auth: "{{ ovirt_auth }}" pattern: "cluster={{ cluster_name }} and host={{ item.name }} and status=up" check_mode: "no" @@ -45,7 +45,6 @@ - description: "Upgrading host: {{ item.name }}" - name: Upgrade host - # TODO: change to ovirt_host, when Ansible 2.8 is out: ovirt_host: auth: "{{ ovirt_auth }}" name: "{{ item.name }}" From 1d3189e2b2c65213ca5652bc642ded9e48b0b0b5 Mon Sep 17 00:00:00 2001 From: mnecas Date: Mon, 11 Nov 2019 12:52:47 +0100 Subject: [PATCH 3/4] register _info modules --- tasks/cluster_policy.yml | 3 ++- tasks/main.yml | 1 + tasks/pinned_vms.yml | 2 +- tasks/upgrade.yml | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tasks/cluster_policy.yml b/tasks/cluster_policy.yml index 077a64e..4cb19ee 100644 --- a/tasks/cluster_policy.yml +++ b/tasks/cluster_policy.yml @@ -3,10 +3,11 @@ auth: "{{ ovirt_auth }}" id: "{{ cluster_info.ovirt_clusters[0].scheduling_policy.id }}" check_mode: "no" + register: sp_info - name: Remember the cluster scheduling policy set_fact: - cluster_scheduling_policy: "{{ ovirt_scheduling_policies[0].name }}" + cluster_scheduling_policy: "{{ sp_info.ovirt_scheduling_policies[0].name }}" - name: Remember the cluster scheduling policy properties set_fact: diff --git a/tasks/main.yml b/tasks/main.yml index 8da8ea9..5e3b0e3 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -101,6 +101,7 @@ auth: "{{ ovirt_auth }}" pattern: "cluster={{ cluster_name }}" check_mode: "no" + register: vms_in_cluster - include_tasks: pinned_vms.yml diff --git a/tasks/pinned_vms.yml b/tasks/pinned_vms.yml index 1ef8a0e..1ca2f30 100644 --- a/tasks/pinned_vms.yml +++ b/tasks/pinned_vms.yml @@ -3,7 +3,7 @@ set_fact: host_ids_items: "{{ item.host.id }}" with_items: - - "{{ ovirt_vms | default([]) }}" + - "{{ vms_in_cluster.ovirt_vms | default([]) }}" when: - "item['placement_policy']['affinity'] != 'migratable'" - "item.host is defined" diff --git a/tasks/upgrade.yml b/tasks/upgrade.yml index bcaa631..2e40eaa 100644 --- a/tasks/upgrade.yml +++ b/tasks/upgrade.yml @@ -15,7 +15,7 @@ when: - "item['placement_policy']['affinity'] == 'user_migratable'" with_items: - - "{{ ovirt_vms }}" + - "{{ vms_in_cluster.ovirt_vms }}" loop_control: label: "{{ item.name }}" @@ -26,7 +26,7 @@ force: true name: "{{ item.name }}" with_items: - - "{{ ovirt_vms }}" + - "{{ vms_in_cluster.ovirt_vms }}" when: - "item['placement_policy']['affinity'] == 'pinned'" loop_control: From 78ea38c5bb8af54187c33c6b3300b1ee5c9d4934 Mon Sep 17 00:00:00 2001 From: mnecas Date: Mon, 11 Nov 2019 14:00:54 +0100 Subject: [PATCH 4/4] remove el8 from automation.yaml Signed-off-by: mnecas --- automation.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/automation.yaml b/automation.yaml index fcc3f1f..0d02263 100644 --- a/automation.yaml +++ b/automation.yaml @@ -1,6 +1,5 @@ distros: - fc30 - el7 - - el8 release_branches: master: [ "ovirt-master" ]