Skip to content

Commit

Permalink
migrating esxi maintenance mode from community
Browse files Browse the repository at this point in the history
  • Loading branch information
mikemorency committed Jan 15, 2025
1 parent 9eb9d27 commit cebef3b
Show file tree
Hide file tree
Showing 15 changed files with 712 additions and 0 deletions.
3 changes: 3 additions & 0 deletions changelogs/fragments/99-migrate-esxi-maintenance-mode.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
minor_changes:
- esxi_maintenance_mode - migrate esxi maintenance module from community
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ action_groups:
- cluster_vcls
- content_library_item_info
- content_template
- esxi_maintenance_mode
- folder_template_from_vm
- guest_info
- license_info
Expand Down
21 changes: 21 additions & 0 deletions plugins/module_utils/_module_pyvmomi_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,24 @@ def get_cluster_by_name_or_moid(self, identifier, fail_on_missing=False, datacen
self.module.fail_json("Unable to find cluster with name or MOID %s" % identifier)

return cluster

def get_esxi_host_by_name_or_moid(self, identifier, fail_on_missing=False):
"""
Get the ESXi host matching the given name or MOID. ESXi names must be unique in a
vCenter, so at most one host is returned.
Args:
identifier: Name or MOID of the ESXi host to search for
fail_on_missing: If true, an error will be thrown if no hosts are found
Returns:
esxi host object or None
"""
esxi_host = self.get_objs_by_name_or_moid(
[vim.HostSystem],
identifier,
return_all=False,
)

if not esxi_host and fail_on_missing:
self.module.fail_json("Unable to find ESXi host with name or MOID %s" % identifier)

return esxi_host
240 changes: 240 additions & 0 deletions plugins/modules/esxi_maintenance_mode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2023, Ansible Cloud Team (@ansible-collections)
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = r'''
---
module: esxi_maintenance_mode
short_description: Manage an ESXi hosts maintenance mode setting in vCenter
description:
- Manage an ESXi hosts maintenance mode setting in vCenter
author:
- Ansible Cloud Team (@ansible-collections)
options:
esxi_host_name:
description:
- Name of the host as defined in vCenter.
required: true
type: str
aliases: ['name']
enable_maintenance_mode:
description:
- If true, the ESXi host will be put into maintenance mode.
required: false
default: true
type: bool
vsan_compliance_mode:
description:
- Specify which VSAN compliant mode to enter.
choices:
- 'ensureObjectAccessibility'
- 'evacuateAllData'
- 'noAction'
required: false
type: str
evacuate:
description:
- If set to V(true), evacuate all powered off VMs.
default: false
required: false
type: bool
timeout:
description:
- Specify a timeout for the operation.
required: false
default: 0
type: int
extends_documentation_fragment:
- vmware.vmware.base_options
'''

EXAMPLES = r'''
- name: Enable ESXi Maintenance Mode On A Host
vmware.vmware.esxi_maintenance_mode:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
name: my_esxi_host
enable_maintenance_mode: true
evacuate: true
timeout: 600
- name: Disable ESXi Maintenance Mode On A Host
vmware.vmware.esxi_maintenance_mode:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
name: my_esxi_host
enable_maintenance_mode: false
- name: Enable With A Specific VSAN Mode
vmware.vmware.esxi_maintenance_mode:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
name: my_esxi_host
enable_maintenance_mode: true
vsan_compliance_mode: ensureObjectAccessibility
'''

RETURN = r'''
result:
description:
- Information about the maintenance mode update task, if something changed
- If nothing changed, an empty dictionary is returned
returned: On success
type: dict
sample: {
"result": {
"completion_time": "2024-07-29T15:27:37.041577+00:00",
"entity_name": "test-5fb1_my_esxi_host",
"error": null,
"state": "success"
}
}
'''

try:
from pyVmomi import vim, vmodl
except ImportError:
pass

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native

from ansible_collections.vmware.vmware.plugins.module_utils._module_pyvmomi_base import (
ModulePyvmomiBase
)
from ansible_collections.vmware.vmware.plugins.module_utils._vmware_argument_spec import (
base_argument_spec
)
from ansible_collections.vmware.vmware.plugins.module_utils._vmware_tasks import (
TaskError,
RunningTaskMonitor
)


class EsxiMaintenanceModeModule(ModulePyvmomiBase):
def __init__(self, module):
super(EsxiMaintenanceModeModule, self).__init__(module)
self.host = self.get_esxi_host_by_name_or_moid(identifier=self.params['esxi_host_name'], fail_on_missing=True)

def current_state_matches_desired_state(self):
"""
Checks the ESXi hosts current maintenance mode setting and compares it to the user's desired maintenance mode
setting.
Returns:
bool, true if they match, otherwise false
"""
if self.params['enable_maintenance_mode'] and self.host.runtime.inMaintenanceMode:
return True
if (not self.params['enable_maintenance_mode']) and (not self.host.runtime.inMaintenanceMode):
return True

return False

def enable_maintenance_mode(self):
"""
Creates a task in vCenter to transition the host into maintenance mode. Waits until the task is complete to
continue.
Returns:
task object describing the maintenance mode transition task
"""
spec = vim.host.MaintenanceSpec()
if self.params['vsan_compliance_mode']:
spec.vsanMode = vim.vsan.host.DecommissionMode()
spec.vsanMode.objectAction = self.params['vsan_compliance_mode']

try:
task = self.host.EnterMaintenanceMode_Task(
self.module.params['timeout'],
self.module.params['evacuate'],
spec
)
_, task_result = RunningTaskMonitor(task).wait_for_completion() # pylint: disable=disallowed-name
except (vmodl.RuntimeFault, vmodl.MethodFault)as vmodl_fault:
self.module.fail_json(msg=to_native(vmodl_fault.msg))
except TaskError as task_e:
self.module.fail_json(msg=to_native(task_e))
except Exception as generic_exc:
self.module.fail_json(msg=(
"Failed to exit maintenance mode on %s due to exception %s" %
(self.params['esxi_host_name'], to_native(generic_exc))
))

return task_result

def disable_maintenance_mode(self):
"""
Creates a task in vCenter to transition the host out of maintenance mode. Waits until the task is complete to
continue.
Returns:
task object describing the maintenance mode transition task
"""
try:
task = self.host.ExitMaintenanceMode_Task(self.module.params['timeout'])
_, task_result = RunningTaskMonitor(task).wait_for_completion() # pylint: disable=disallowed-name
except (vmodl.RuntimeFault, vmodl.MethodFault)as vmodl_fault:
self.module.fail_json(msg=to_native(vmodl_fault.msg))
except TaskError as task_e:
self.module.fail_json(msg=to_native(task_e))
except Exception as generic_exc:
self.module.fail_json(msg=(
"Failed to exit maintenance mode on %s due to exception %s" %
(self.params['esxi_host_name'], to_native(generic_exc))
))

return task_result


def main():
module = AnsibleModule(
argument_spec={
**base_argument_spec(), **dict(
esxi_host_name=dict(type='str', required=True, aliases=['name']),
vsan_compliance_mode=dict(type='str', required=False, choices=['ensureObjectAccessibility', 'evacuateAllData', 'noAction']),
enable_maintenance_mode=dict(type='bool', default=True),
evacuate=dict(type='bool', default=False),
timeout=dict(type='int', default=0),
)
},
supports_check_mode=True,
)

result = dict(
changed=False,
result={}
)

esxi_maint_mode = EsxiMaintenanceModeModule(module)
if esxi_maint_mode.current_state_matches_desired_state():
module.exit_json(**result)

result['changed'] = True
if module.check_mode:
module.exit_json(**result)

if module.params['enable_maintenance_mode']:
result['result'] = esxi_maint_mode.enable_maintenance_mode()
else:
result['result'] = esxi_maint_mode.disable_maintenance_mode()

# this field has the ESXi host object in it, which can't be output by ansible without manipulation.
# but we dont need it in the output anyway, so just delete it
del result['result']['result']

module.exit_json(**result)


if __name__ == '__main__':
main()
4 changes: 4 additions & 0 deletions tests/integration/requirements.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
collections:
- name: vmware.vmware_rest
version: ">=4.4.0"
5 changes: 5 additions & 0 deletions tests/integration/targets/group_vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
# please edit according to your vcenter configs
vcenter_cluster_name: "Eco-Cluster"
vcenter_datacenter: "Eco-Datacenter"
vcenter_resource_pool: "Resources"
vcenter_port: 443

shared_storage_01: "eco-iscsi-ds1"
shared_storage_02: "eco-iscsi-ds2"

ci_resources_content_library: ansible_ci_test_resources
esxi_content_library_template: esxi-8
2 changes: 2 additions & 0 deletions tests/integration/targets/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ fi
echo "ANSIBLE_COLLECTIONS_PATH: $ANSIBLE_COLLECTIONS_PATH"
BASE_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")")
export ANSIBLE_ROLES_PATH=${BASE_DIR}

ansible-galaxy collection install --upgrade -r ${BASE_DIR}/../requirements.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
test_esxi_hostname: "{{ tiny_prefix }}_esxi_maintenance_mode"
test_resource_pool: "{{ tiny_prefix }}_vmware_esxi_maintenance_mode"

run_on_simulator: false
27 changes: 27 additions & 0 deletions tests/integration/targets/vmware_esxi_maintenance_mode/run.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
- hosts: localhost
gather_facts: no
collections:
- community.general

tasks:
- name: Import eco-vcenter credentials
ansible.builtin.include_vars:
file: ../../integration_config.yml
tags: eco-vcenter-ci

- name: Import simulator vars
ansible.builtin.include_vars:
file: vars.yml
tags: integration-ci

- name: Vcsim
ansible.builtin.import_role:
name: prepare_vcsim
tags: integration-ci

- name: Import vmware_esxi_maintenance_mode role
ansible.builtin.import_role:
name: vmware_esxi_maintenance_mode
tags:
- integration-ci
- eco-vcenter-ci
Loading

0 comments on commit cebef3b

Please sign in to comment.