From 937fc1421b225d9f2c661f285cd28c0d7907a7b1 Mon Sep 17 00:00:00 2001 From: Ondra Machacek Date: Wed, 25 Jan 2017 15:17:32 +0100 Subject: [PATCH] Allow change kernel boot parameters of oVirt hosts (#20607) * cloud: ovirt: add override_display parameter * cloud: ovirt: add kernel_params parameter * cloud: ovirt: add reinstall state to ovirt_hosts module * cloud: ovirt: wait before polling entity state --- lib/ansible/module_utils/ovirt.py | 6 +- .../modules/cloud/ovirt/ovirt_hosts.py | 103 ++++++++++++++++-- 2 files changed, 95 insertions(+), 14 deletions(-) diff --git a/lib/ansible/module_utils/ovirt.py b/lib/ansible/module_utils/ovirt.py index c74aafb8ab9..84bfdd0e392 100644 --- a/lib/ansible/module_utils/ovirt.py +++ b/lib/ansible/module_utils/ovirt.py @@ -283,6 +283,9 @@ def wait( if wait: start = time.time() while time.time() < start + timeout: + # Sleep for `poll_interval` seconds if none of the conditions apply: + time.sleep(float(poll_interval)) + # Exit if the condition of entity is valid: entity = get_entity(service) if condition(entity): @@ -290,9 +293,6 @@ def wait( elif fail_condition(entity): raise Exception("Error while waiting on result state of the entity.") - # Sleep for `poll_interval` seconds if nor of the conditions apply: - time.sleep(float(poll_interval)) - def __get_auth_dict(): OVIRT_URL = os.environ.get('OVIRT_URL') diff --git a/lib/ansible/modules/cloud/ovirt/ovirt_hosts.py b/lib/ansible/modules/cloud/ovirt/ovirt_hosts.py index 2e1ea540370..06bfa1824b2 100644 --- a/lib/ansible/modules/cloud/ovirt/ovirt_hosts.py +++ b/lib/ansible/modules/cloud/ovirt/ovirt_hosts.py @@ -59,7 +59,10 @@ options: state: description: - "State which should a host to be in after successful completion." - choices: ['present', 'absent', 'maintenance', 'upgraded', 'started', 'restarted', 'stopped'] + choices: [ + 'present', 'absent', 'maintenance', 'upgraded', 'started', + 'restarted', 'stopped', 'reinstalled' + ] default: present comment: description: @@ -94,6 +97,23 @@ options: description: - "If True host will be forcibly moved to desired state." default: False + override_display: + description: + - "Override the display address of all VMs on this host with specified address." + 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) suceesfully and then to be I(rebooted) for kernel boot parameters + to be applied." extends_documentation_fragment: ovirt ''' @@ -101,12 +121,14 @@ 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 +# Add host with username/password supporting SR-IOV: - ovirt_hosts: cluster: Default name: myhost address: 10.34.61.145 password: secret + kernel_params: + - intel_iommu=on # Add host using public key - ovirt_hosts: @@ -171,13 +193,25 @@ class HostsModule(BaseModule): priority=self._module.params['spm_priority'], ) if self._module.params['spm_priority'] else None, override_iptables=self._module.params['override_iptables'], + display=otypes.Display( + address=self._module.params['override_display'], + ) if self._module.params['override_display'] else None, + os=otypes.OperatingSystem( + custom_kernel_cmdline=' '.join(self._module.params['kernel_params']), + ) if self._module.params['kernel_params'] else None, ) def update_check(self, entity): + kernel_params = self._module.params.get('kernel_params') return ( equal(self._module.params.get('comment'), entity.comment) and equal(self._module.params.get('kdump_integration'), entity.kdump_status) and - equal(self._module.params.get('spm_priority'), entity.spm.priority) + equal(self._module.params.get('spm_priority'), entity.spm.priority) and + equal(self._module.params.get('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): @@ -189,7 +223,7 @@ class HostsModule(BaseModule): ) def post_update(self, entity): - if entity.status != hoststate.UP: + if entity.status != hoststate.UP and self._module.params['state'] == 'present': if not self._module.check_mode: self._service.host_service(entity.id).activate() self.changed = True @@ -209,9 +243,17 @@ def control_state(host_module): if host is None: return + state = host_module._module.params['state'] host_service = host_module._service.service(host.id) if failed_state(host): - raise Exception("Not possible to manage host '%s'." % host.name) + # 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, @@ -235,7 +277,10 @@ def control_state(host_module): def main(): argument_spec = ovirt_full_argument_spec( state=dict( - choices=['present', 'absent', 'maintenance', 'upgraded', 'started', 'restarted', 'stopped'], + choices=[ + 'present', 'absent', 'maintenance', 'upgraded', 'started', + 'restarted', 'stopped', 'reinstalled', + ], default='present', ), name=dict(required=True), @@ -249,6 +294,8 @@ def main(): 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'), ) module = AnsibleModule( argument_spec=argument_spec, @@ -269,12 +316,12 @@ def main(): control_state(hosts_module) if state == 'present': ret = hosts_module.create() - hosts_module.action( + ret['changed'] = 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, - ) + )['changed'] or ret['changed'] elif state == 'absent': ret = hosts_module.remove() elif state == 'maintenance': @@ -284,6 +331,7 @@ def main(): wait_condition=lambda h: h.status == hoststate.MAINTENANCE, fail_condition=failed_state, ) + ret['changed'] = hosts_module.create()['changed'] or ret['changed'] elif state == 'upgraded': ret = hosts_module.action( action='upgrade', @@ -300,19 +348,19 @@ def main(): fence_type='start', ) elif state == 'stopped': - hosts_module.action( + ret = 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( + ret['changed'] = hosts_module.action( action='fence', action_condition=lambda h: h.status != hoststate.DOWN, wait_condition=lambda h: h.status == hoststate.DOWN, fail_condition=failed_state, fence_type='stop', - ) + )['changed'] or ret['changed'] elif state == 'restarted': ret = hosts_module.action( action='fence', @@ -320,6 +368,39 @@ def main(): fail_condition=failed_state, fence_type='restart', ) + elif state == 'reinstalled': + # Deactivate host if not in maintanence: + deactivate_changed = 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, + )['changed'] + + # Reinstall host: + install_changed = hosts_module.action( + action='install', + action_condition=lambda h: h.status == hoststate.MAINTENANCE, + wait_condition=lambda h: h.status == hoststate.MAINTENANCE, + fail_condition=failed_state, + 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='publickey', + ) if module.params['public_key'] else None, + )['changed'] + + # 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, + ) + ret['changed'] = install_changed or deactivate_changed or ret['changed'] + module.exit_json(**ret) except Exception as e: