Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/migrate esxi maintenance mode #99

Merged
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
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 @@
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(

Check warning on line 291 in plugins/module_utils/_module_pyvmomi_base.py

View check run for this annotation

Codecov / codecov/patch

plugins/module_utils/_module_pyvmomi_base.py#L291

Added line #L291 was not covered by tests
[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)

Check warning on line 298 in plugins/module_utils/_module_pyvmomi_base.py

View check run for this annotation

Codecov / codecov/patch

plugins/module_utils/_module_pyvmomi_base.py#L298

Added line #L298 was not covered by tests

return esxi_host

Check warning on line 300 in plugins/module_utils/_module_pyvmomi_base.py

View check run for this annotation

Codecov / codecov/patch

plugins/module_utils/_module_pyvmomi_base.py#L300

Added line #L300 was not covered by tests
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",
anna-savina marked this conversation as resolved.
Show resolved Hide resolved
"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)

Check warning on line 130 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L129-L130

Added lines #L129 - L130 were not covered by tests

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

Check warning on line 140 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L140

Added line #L140 was not covered by tests
if (not self.params['enable_maintenance_mode']) and (not self.host.runtime.inMaintenanceMode):
return True

Check warning on line 142 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L142

Added line #L142 was not covered by tests

return False

Check warning on line 144 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L144

Added line #L144 was not covered by tests

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()

Check warning on line 153 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L153

Added line #L153 was not covered by tests
if self.params['vsan_compliance_mode']:
spec.vsanMode = vim.vsan.host.DecommissionMode()
spec.vsanMode.objectAction = self.params['vsan_compliance_mode']

Check warning on line 156 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L155-L156

Added lines #L155 - L156 were not covered by tests

try:
task = self.host.EnterMaintenanceMode_Task(

Check warning on line 159 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L158-L159

Added lines #L158 - L159 were not covered by tests
self.module.params['timeout'],
self.module.params['evacuate'],
spec
)
_, task_result = RunningTaskMonitor(task).wait_for_completion() # pylint: disable=disallowed-name

Check warning on line 164 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L164

Added line #L164 was not covered by tests
except (vmodl.RuntimeFault, vmodl.MethodFault)as vmodl_fault:
self.module.fail_json(msg=to_native(vmodl_fault.msg))

Check warning on line 166 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L166

Added line #L166 was not covered by tests
except TaskError as task_e:
self.module.fail_json(msg=to_native(task_e))
except Exception as generic_exc:
self.module.fail_json(msg=(

Check warning on line 170 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L168-L170

Added lines #L168 - L170 were not covered by tests
"Failed to exit maintenance mode on %s due to exception %s" %
(self.params['esxi_host_name'], to_native(generic_exc))
))

return task_result

Check warning on line 175 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L175

Added line #L175 was not covered by tests

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

Check warning on line 186 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L184-L186

Added lines #L184 - L186 were not covered by tests
except (vmodl.RuntimeFault, vmodl.MethodFault)as vmodl_fault:
self.module.fail_json(msg=to_native(vmodl_fault.msg))

Check warning on line 188 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L188

Added line #L188 was not covered by tests
except TaskError as task_e:
self.module.fail_json(msg=to_native(task_e))
except Exception as generic_exc:
self.module.fail_json(msg=(

Check warning on line 192 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L190-L192

Added lines #L190 - L192 were not covered by tests
"Failed to exit maintenance mode on %s due to exception %s" %
(self.params['esxi_host_name'], to_native(generic_exc))
))

return task_result

Check warning on line 197 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L197

Added line #L197 was not covered by tests


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(

Check warning on line 214 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L214

Added line #L214 was not covered by tests
changed=False,
result={}
)

esxi_maint_mode = EsxiMaintenanceModeModule(module)

Check warning on line 219 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L219

Added line #L219 was not covered by tests
if esxi_maint_mode.current_state_matches_desired_state():
module.exit_json(**result)

Check warning on line 221 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L221

Added line #L221 was not covered by tests

result['changed'] = True

Check warning on line 223 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L223

Added line #L223 was not covered by tests
if module.check_mode:
module.exit_json(**result)

Check warning on line 225 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L225

Added line #L225 was not covered by tests

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

Check warning on line 228 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L228

Added line #L228 was not covered by tests
else:
result['result'] = esxi_maint_mode.disable_maintenance_mode()

Check warning on line 230 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L230

Added line #L230 was not covered by tests

# 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']

Check warning on line 234 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L234

Added line #L234 was not covered by tests

module.exit_json(**result)

Check warning on line 236 in plugins/modules/esxi_maintenance_mode.py

View check run for this annotation

Codecov / codecov/patch

plugins/modules/esxi_maintenance_mode.py#L236

Added line #L236 was not covered by tests


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
Loading