Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelogs/fragments/2342-vmware_drs_override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
minor_changes:
- vmware_drs_override - Add new state attribute with present, absent, disabled options.
You can now remove existing DRS override or set DRS Override to disabled.
Add option to chose VM with name, uuid or moid (https://github.com/ansible-collections/community.vmware/pull/2342).
175 changes: 141 additions & 34 deletions plugins/modules/vmware_drs_override.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,90 @@
description:
- This module allows setting a DRS behavior override for individual VMs within a DRS-enabled VMware vSphere cluster.
options:
vm_name:
description:
- Name of the VM for which the DRS override is set.
required: true
type: str
name:
description:
- Name of the VM for which the DRS override is set.
- This is required if O(uuid) or O(moid) is not supplied.
type: str
aliases: [ vm_name ]
uuid:
description:
- UUID of the instance to manage if known, this is VMware's unique identifier.
- This is required if O(name) or O(moid) is not supplied.
type: str
use_instance_uuid:
description:
- Whether to use the VMware instance UUID rather than the BIOS UUID.
default: false
type: bool
moid:
description:
- Managed Object ID of the instance to manage if known, this is a unique identifier only within a single vCenter instance.
- This is required if O(name) or O(uuid) is not supplied.
type: str
folder:
description:
- Destination folder, absolute or relative path to find an existing guest.
- The folder should include the datacenter. ESXi server's datacenter is ha-datacenter.
- 'Examples:'
- ' folder: /ha-datacenter/vm'
- ' folder: ha-datacenter/vm'
- ' folder: /datacenter1/vm'
- ' folder: datacenter1/vm'
- ' folder: /datacenter1/vm/folder1'
- ' folder: datacenter1/vm/folder1'
- ' folder: /folder1/datacenter1/vm'
- ' folder: folder1/datacenter1/vm'
- ' folder: /folder1/datacenter1/vm/folder2'
type: str
datacenter:
description:
- The datacenter name to which virtual machine belongs to.
type: str
drs_behavior:
description:
- Desired DRS behavior for the VM.
- manual: DRS generates both power-on placement recommendation, and migration recommendation for VM. Recommendations need to be manually applied or ignored.
- partiallyAutomated: DRS automatically place VM onto host at VM power-on. Migration recommendations need to be manually applied or ignored.
- fullyAutomated: DRS automatically place VM onto host at VM power-on, and VM is automatically migrated from one host to another to optimize resource utilization.
choices: ['manual', 'partiallyAutomated', 'fullyAutomated']
default: 'manual'
type: str
state:
description:
- State of the override setting.
- disabled: You will disable DRS automation completely for this VM.
- absent: You will remove the DRS override.
choices: ['present', 'absent', 'disabled']
default: 'present'
type: str
extends_documentation_fragment:
- community.vmware.vmware.documentation
author:
- Sergey Goncharov (@svg1007)
- Michał Gąsior (@Rogacz)
'''

EXAMPLES = '''
- name: Set DRS behavior for a VM
vmware_drs_override:
community.vmware.vmware_drs_override:
hostname: "vcenter.example.com"
username: "[email protected]"
password: "yourpassword"
port: 443
validate_certs: False
vm_name: "my_vm_name"
name: "my_vm_name"
drs_behavior: "manual"
delegate_to: localhost

- name: Remove DRS override for a VM
community.vmware.vmware_drs_override:
hostname: "vcenter.example.com"
username: "[email protected]"
password: "yourpassword"
moid: vm-42
state: absent
delegate_to: localhost
'''

RETURN = '''
Expand All @@ -65,57 +122,107 @@
class VmwareDrsOverride(PyVmomi):
def __init__(self, module):
super(VmwareDrsOverride, self).__init__(module)
self.vm_name = self.params.get('vm_name', None)
self.drs_behavior = module.params['drs_behavior']
self.params['name'] = self.vm_name
self.drs_state = module.params['state']
self.drs_enabled = self.drs_state == 'present'
self.drs_vm_config_spec = None
self.msg = "Unexpected exit."
self.vm = self.get_vm()
if not self.vm:
self.module.fail_json(msg="VM '%s' not found." % self.vm_name)

self.module.fail_json(msg=f"VM '{self.params['name']}' not found.")
if not self.is_vcenter():
self.module.fail_json(msg="DRS configuration is only supported in vCenter environments.")
self.cluster = self.vm.runtime.host.parent
if not self.cluster:
self.module.fail_json(msg="VM is not in a Cluster.")

def set_drs_override(self):
cluster = self.vm.runtime.host.parent

# Check current DRS settings
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.")

# Create DRS VM config spec
drs_vm_config_spec = vim.cluster.DrsVmConfigSpec(
operation='add',
info=vim.cluster.DrsVmConfigInfo(
key=self.vm,
enabled=True,
behavior=self.drs_behavior
existing_config = next((config for config in self.cluster.configuration.drsVmConfig if config.key == self.vm), None)
if existing_config:
if self.drs_state == 'absent':
# Remove the DRS override
self.drs_vm_config_spec = vim.cluster.DrsVmConfigSpec(
operation=vim.option.ArrayUpdateSpec.Operation.remove,
removeKey=self.vm,
)
self.msg = "DRS override removed successfully."
self.execute()
if existing_config.behavior == self.drs_behavior and existing_config.enabled == self.drs_enabled:
# Nothing to do
self.module.exit_json(changed=False, msg="DRS behavior is already set to the desired state.")
else:
# Update the DRS override
self.drs_vm_config_spec = vim.cluster.DrsVmConfigSpec(
operation=vim.option.ArrayUpdateSpec.Operation.edit,
info=vim.cluster.DrsVmConfigInfo(
key=self.vm,
enabled=self.drs_enabled,
behavior=self.drs_behavior,
),
)
self.msg = "DRS override updated successfully."
self.execute()
else:
if self.drs_state == 'absent':
# Nothing to do
self.module.exit_json(changed=False, msg="DRS override is already absent.")
# Define the DRS override as it does not exist
self.drs_vm_config_spec = vim.cluster.DrsVmConfigSpec(
operation=vim.option.ArrayUpdateSpec.Operation.add,
info=vim.cluster.DrsVmConfigInfo(
key=self.vm,
enabled=self.drs_enabled,
behavior=self.drs_behavior,
),
)
)
self.msg = "DRS override applied successfully."

self.execute()

def execute(self):
if self.module.check_mode:
# Exit if in check mode
self.module.exit_json(changed=True, msg=self.msg)

# Apply the cluster reconfiguration
cluster_config_spec = vim.cluster.ConfigSpec()
cluster_config_spec.drsVmConfigSpec = [drs_vm_config_spec]
cluster_config_spec.drsVmConfigSpec = [self.drs_vm_config_spec]
try:
task = cluster.ReconfigureCluster_Task(spec=cluster_config_spec, modify=True)
task = self.cluster.ReconfigureCluster_Task(spec=cluster_config_spec, modify=True)
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)
self.module.fail_json(msg=f"Failed to set DRS override: {error.msg}")

self.module.exit_json(changed=True, msg=self.msg)


def main():
argument_spec = base_argument_spec()
argument_spec.update(dict(
vm_name=dict(type='str', required=True),
drs_behavior=dict(type='str', choices=['manual', 'partiallyAutomated', 'fullyAutomated'], default='manual')
))
argument_spec.update({
'name': {'type': 'str', 'aliases': ['vm_name']},
'uuid': {'type': 'str'},
'moid': {'type': 'str'},
'use_instance_uuid': {'type': 'bool', 'default': False},
'folder': {'type': 'str'},
'datacenter': {'type': 'str'},
'drs_behavior': {'type': 'str', 'choices': ['manual', 'partiallyAutomated', 'fullyAutomated'], 'default': 'manual'},
'state': {'type': 'str', 'choices': ['present', 'absent', 'disabled'], 'default': 'present'},
})

module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True
supports_check_mode=True,
required_one_of=[
['name', 'uuid', 'moid'],
],
)

if module.params['folder']:
# FindByInventoryPath() does not require an absolute path
# so we should leave the input folder path unmodified
module.params['folder'] = module.params['folder'].rstrip('/')

drs_override = VmwareDrsOverride(module)
drs_override.set_drs_override()

Expand Down
38 changes: 36 additions & 2 deletions tests/integration/targets/vmware_drs_override/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@
- name: Set fact for the first VM name
set_fact:
first_vm_name: "{{ vm_info.virtual_machines[0].guest_name }}"
first_vm_moid: "{{ vm_info.virtual_machines[0].moid }}"
first_vm_cluster: "{{ vm_info.virtual_machines[0].cluster }}"

- name: Gather info about the first vm cluster
vmware_cluster_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
validate_certs: false
cluster_name: "{{ first_vm_cluster }}"
register: cluster_result

- name: Set fact for the DRS
set_fact:
drs_enabled: "{{ cluster_result['clusters'][first_vm_cluster].enabled_drs }}"

# Test case: Add DRS Override - DRS enabled
- name: Add DRS override 'manual' for a VM in a DRS-enabled cluster
Expand All @@ -38,6 +53,25 @@
- "'DRS override applied successfully' in drs_override_result.msg"
when: drs_enabled is defined and drs_enabled

# Test case: Remove DRS Override - DRS enabled
- name: Remove DRS override for a VM in a DRS-enabled cluster
vmware_drs_override:
validate_certs: false
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
moid: "{{ first_vm_moid }}"
state: "absent"
register: drs_override_remove_result
when: drs_enabled is defined and drs_enabled

- name: Assert DRS override removed successfully
assert:
that:
- drs_override_remove_result.changed == true
- "'DRS override removed successfully' in drs_override_remove_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:
Expand Down Expand Up @@ -69,11 +103,11 @@
drs_behavior: "manual"
register: drs_override_drs_disabled_result
ignore_errors: true
when: drs_disabled is defined and drs_disabled
when: drs_enabled is defined and drs_enabled == false

- 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
when: drs_enabled is defined and drs_enabled == false