From 663047cbd3650b97a69bf92b78df78689175fa6f Mon Sep 17 00:00:00 2001 From: Sergey Goncharov Date: Thu, 31 Oct 2024 15:20:40 +0200 Subject: [PATCH 1/5] Add VM DRS Override plugin --- meta/runtime.yml | 1 + plugins/modules/vmware_drs_override.py | 156 +++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 plugins/modules/vmware_drs_override.py diff --git a/meta/runtime.yml b/meta/runtime.yml index c9d6e531a..1709700bf 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -40,6 +40,7 @@ action_groups: - vmware_drs_group - vmware_drs_group_info - vmware_drs_group_manager + - vmware_drs_override - vmware_drs_rule_info - vmware_dvs_host - vmware_dvs_portgroup diff --git a/plugins/modules/vmware_drs_override.py b/plugins/modules/vmware_drs_override.py new file mode 100644 index 000000000..e3cad230f --- /dev/null +++ b/plugins/modules/vmware_drs_override.py @@ -0,0 +1,156 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +DOCUMENTATION = ''' +--- +module: vmware_drs_override +short_description: Configure DRS behavior for a specific VM in vSphere +description: + - This module allows setting a DRS behavior override for individual VMs within a DRS-enabled VMware vSphere cluster. +options: + hostname: + description: + - The hostname or IP address of the vCenter server. + required: true + type: str + username: + description: + - The username for vCenter authentication. + required: true + type: str + password: + description: + - The password for vCenter authentication. + required: true + type: str + port: + description: + - The port number for the vCenter server. + required: false + type: int + default: 443 + validate_certs: + description: + - If C(false), SSL certificates will not be validated. + type: bool + default: False + vm_name: + description: + - Name of the VM for which the DRS override is set. + required: true + type: str + drs_behavior: + description: + - Desired DRS behavior for the VM. + choices: ['manual', 'partiallyAutomated', 'fullyAutomated'] + default: 'manual' + type: str +author: + - Your Name (@your_github_username) +''' + +EXAMPLES = ''' +- name: Set DRS behavior for a VM + vmware_drs_override: + hostname: "vcenter.example.com" + username: "administrator@vsphere.local" + password: "yourpassword" + port: 443 + validate_certs: False + vm_name: "my_vm_name" + drs_behavior: "manual" +''' + +RETURN = ''' +changed: + description: Whether the DRS behavior was changed. + type: bool + returned: always +msg: + description: A message describing the outcome of the task. + type: str + returned: always +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.vmware.plugins.module_utils.vmware import ( + PyVmomi, + connect_to_api, + vmware_argument_spec, +) +from pyVmomi import vim, vmodl + + +class VmwareDrsOverride(PyVmomi): + def __init__(self, module): + super(VmwareDrsOverride, self).__init__(module) + self.module = module + self.vm_name = module.params['vm_name'] + self.drs_behavior = module.params['drs_behavior'] + self.content = self.si.RetrieveContent() + + def get_vm_by_name(self): + obj_view = self.content.viewManager.CreateContainerView(self.content.rootFolder, [vim.VirtualMachine], True) + for vm in obj_view.view: + if vm.name == self.vm_name: + obj_view.Destroy() + return vm + obj_view.Destroy() + self.module.fail_json(msg="VM '%s' not found." % self.vm_name) + + def set_drs_override(self): + vm = self.get_vm_by_name() + cluster = vm.runtime.host.parent + + # Check current DRS settings + existing_config = next((config for config in cluster.configuration.drsVmConfig if config.key == vm), None) + if existing_config and existing_config.behavior == self.drs_behavior: + self.module.exit_json(changed=False, msg="DRS behavior is already set to the desired state.") + + # Create DRS VM config spec + drs_vm_config_spec = vim.cluster.DrsVmConfigSpec( + operation='add', + info=vim.cluster.DrsVmConfigInfo( + key=vm, + enabled=True, + behavior=self.drs_behavior + ) + ) + + # Apply the cluster reconfiguration + cluster_config_spec = vim.cluster.ConfigSpec() + cluster_config_spec.drsVmConfigSpec = [drs_vm_config_spec] + try: + task = cluster.ReconfigureCluster_Task(spec=cluster_config_spec) + self.wait_for_task(task) + self.module.exit_json(changed=True, msg="DRS override applied successfully.") + except vmodl.MethodFault as error: + self.module.fail_json(msg="Failed to set DRS override: %s" % error.msg) + + def wait_for_task(self, task): + while task.info.state == vim.TaskInfo.State.running: + pass + if task.info.state == vim.TaskInfo.State.success: + return task.info.result + else: + raise Exception("Task failed: %s" % task.info.error.localizedMessage) + + +def main(): + argument_spec = vmware_argument_spec() + argument_spec.update(dict( + vm_name=dict(type='str', required=True), + drs_behavior=dict(type='str', choices=['manual', 'partiallyAutomated', 'fullyAutomated'], default='manual') + )) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + drs_override = VmwareDrsOverride(module) + drs_override.set_drs_override() + + +if __name__ == '__main__': + main() From 2ff11e96c2a4913920f19eb369a40a01fedb92b7 Mon Sep 17 00:00:00 2001 From: Sergey Goncharov Date: Thu, 31 Oct 2024 21:29:56 +0200 Subject: [PATCH 2/5] Add VM DRS Override plugin - replace get_vm_by_name by get_vm --- plugins/modules/vmware_drs_override.py | 29 +++++++++----------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/plugins/modules/vmware_drs_override.py b/plugins/modules/vmware_drs_override.py index e3cad230f..172c2463e 100644 --- a/plugins/modules/vmware_drs_override.py +++ b/plugins/modules/vmware_drs_override.py @@ -46,7 +46,7 @@ default: 'manual' type: str author: - - Your Name (@your_github_username) + - Sergey Goncharov (@svg1007) ''' EXAMPLES = ''' @@ -75,7 +75,6 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.vmware.plugins.module_utils.vmware import ( PyVmomi, - connect_to_api, vmware_argument_spec, ) from pyVmomi import vim, vmodl @@ -84,26 +83,18 @@ class VmwareDrsOverride(PyVmomi): def __init__(self, module): super(VmwareDrsOverride, self).__init__(module) - self.module = module - self.vm_name = module.params['vm_name'] + self.vm_name = self.params.get('vm_name', None) self.drs_behavior = module.params['drs_behavior'] - self.content = self.si.RetrieveContent() - - def get_vm_by_name(self): - obj_view = self.content.viewManager.CreateContainerView(self.content.rootFolder, [vim.VirtualMachine], True) - for vm in obj_view.view: - if vm.name == self.vm_name: - obj_view.Destroy() - return vm - obj_view.Destroy() - self.module.fail_json(msg="VM '%s' not found." % self.vm_name) + self.params['name'] = self.vm_name + self.vm = self.get_vm() + if not self.vm: + self.module.fail_json(msg="VM '%s' not found." % self.vm_name) def set_drs_override(self): - vm = self.get_vm_by_name() - cluster = vm.runtime.host.parent + cluster = self.vm.runtime.host.parent # Check current DRS settings - existing_config = next((config for config in cluster.configuration.drsVmConfig if config.key == vm), None) + existing_config = next((config for config in cluster.configuration.drsVmConfig if config.key == self.vm), None) if existing_config and existing_config.behavior == self.drs_behavior: self.module.exit_json(changed=False, msg="DRS behavior is already set to the desired state.") @@ -111,7 +102,7 @@ def set_drs_override(self): drs_vm_config_spec = vim.cluster.DrsVmConfigSpec( operation='add', info=vim.cluster.DrsVmConfigInfo( - key=vm, + key=self.vm, enabled=True, behavior=self.drs_behavior ) @@ -121,7 +112,7 @@ def set_drs_override(self): cluster_config_spec = vim.cluster.ConfigSpec() cluster_config_spec.drsVmConfigSpec = [drs_vm_config_spec] try: - task = cluster.ReconfigureCluster_Task(spec=cluster_config_spec) + task = cluster.ReconfigureCluster_Task(spec=cluster_config_spec, modify=True) self.wait_for_task(task) self.module.exit_json(changed=True, msg="DRS override applied successfully.") except vmodl.MethodFault as error: From eef397e51e596e40bfe7fa9d02191d6d4a56d5b9 Mon Sep 17 00:00:00 2001 From: Sergey Goncharov Date: Fri, 1 Nov 2024 11:08:34 +0200 Subject: [PATCH 3/5] Add VM DRS Override plugin - add tests --- plugins/modules/vmware_drs_override.py | 3 + .../targets/vmware_drs_override/aliases | 3 + .../vmware_drs_override/tasks/main.yml | 80 +++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 tests/integration/targets/vmware_drs_override/aliases create mode 100644 tests/integration/targets/vmware_drs_override/tasks/main.yml diff --git a/plugins/modules/vmware_drs_override.py b/plugins/modules/vmware_drs_override.py index 172c2463e..c3c0cad96 100644 --- a/plugins/modules/vmware_drs_override.py +++ b/plugins/modules/vmware_drs_override.py @@ -90,6 +90,9 @@ def __init__(self, module): if not self.vm: self.module.fail_json(msg="VM '%s' not found." % self.vm_name) + if not self.is_vcenter(): + self.module.fail_json(msg="DRS configuration is only supported in vCenter environments.") + def set_drs_override(self): cluster = self.vm.runtime.host.parent diff --git a/tests/integration/targets/vmware_drs_override/aliases b/tests/integration/targets/vmware_drs_override/aliases new file mode 100644 index 000000000..07e8732a3 --- /dev/null +++ b/tests/integration/targets/vmware_drs_override/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_drs_override/tasks/main.yml b/tests/integration/targets/vmware_drs_override/tasks/main.yml new file mode 100644 index 000000000..6a8ab203a --- /dev/null +++ b/tests/integration/targets/vmware_drs_override/tasks/main.yml @@ -0,0 +1,80 @@ +# Test code for the vmware_drs_override module + +- import_role: + name: prepare_vmware_tests + vars: + setup_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +# Gather information about available VMs +- name: Get info about available VMs + vmware_vm_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + register: vm_info + +- name: Set fact for the first VM name + set_fact: + first_vm_name: "{{ vm_info.virtual_machines[0].guest_name }}" + +# Test case: Add DRS Override - DRS enabled +- name: Add DRS override 'manual' for a VM in a DRS-enabled cluster + vmware_drs_override: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + vm_name: "{{ first_vm_name }}" + drs_behavior: "manual" + register: drs_override_result + when: drs_enabled is defined and drs_enabled + +- name: Assert DRS override applied successfully + assert: + that: + - drs_override_result.changed == true + - "'DRS override applied successfully' in drs_override_result.msg" + when: drs_enabled is defined and drs_enabled + +# Test case: Ensure proper error for standalone ESXi without DRS +- name: Attempt to add DRS override for VM in a non-DRS environment + vmware_drs_override: + validate_certs: false + hostname: "{{ standalone_esxi_hostname }}" + username: "{{ esxi_username }}" + password: "{{ esxi_password }}" + vm_name: "{{ first_vm_name }}" + drs_behavior: "manual" + register: drs_override_non_drs_result + ignore_errors: true + when: standalone_esxi_hostname is defined + +- name: Assert error for non-DRS environment + assert: + that: + - drs_override_non_drs_result.failed == true + - "'DRS configuration is only supported in vCenter environments' in drs_override_non_drs_result.msg" + when: standalone_esxi_hostname is defined + +# Test case: Check behavior for a vCenter cluster with DRS disabled +- name: Attempt to add DRS override for VM in a vCenter cluster with DRS disabled + vmware_drs_override: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + vm_name: "{{ first_vm_name }}" + drs_behavior: "manual" + register: drs_override_drs_disabled_result + ignore_errors: true + when: drs_disabled is defined and drs_disabled + +- name: Assert error for DRS-disabled cluster + assert: + that: + - drs_override_drs_disabled_result.failed == true + - "'DRS is not enabled on the cluster' in drs_override_drs_disabled_result.msg" + when: drs_disabled is defined and drs_disabled From a384759cc588e6551a925c585744bdef13b85ef4 Mon Sep 17 00:00:00 2001 From: Sergey Goncharov Date: Fri, 1 Nov 2024 11:14:00 +0200 Subject: [PATCH 4/5] Add VM DRS Override plugin - add tests --- plugins/modules/vmware_drs_override.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/vmware_drs_override.py b/plugins/modules/vmware_drs_override.py index c3c0cad96..eea416870 100644 --- a/plugins/modules/vmware_drs_override.py +++ b/plugins/modules/vmware_drs_override.py @@ -92,7 +92,7 @@ def __init__(self, module): if not self.is_vcenter(): self.module.fail_json(msg="DRS configuration is only supported in vCenter environments.") - + def set_drs_override(self): cluster = self.vm.runtime.host.parent From 012137b320c322eda094014c01bd018f4db19ded Mon Sep 17 00:00:00 2001 From: Sergey Goncharov Date: Fri, 1 Nov 2024 11:52:51 +0200 Subject: [PATCH 5/5] Add VM DRS Override plugin - add tests --- tests/integration/targets/vmware_drs_override/tasks/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/targets/vmware_drs_override/tasks/main.yml b/tests/integration/targets/vmware_drs_override/tasks/main.yml index 6a8ab203a..00f14d342 100644 --- a/tests/integration/targets/vmware_drs_override/tasks/main.yml +++ b/tests/integration/targets/vmware_drs_override/tasks/main.yml @@ -4,7 +4,6 @@ name: prepare_vmware_tests vars: setup_attach_host: true - setup_datastore: true setup_virtualmachines: true # Gather information about available VMs