mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-28 11:10:21 -07:00
vmware_host: add reconnect and add_or_reconnect states (#30582)
* vmware_host: add reconnect and add_or_reconnect states Add "reconnect" and "add_or_reconnect" choices for "state". * reconnect: reconnect an esxi to a vcenter (imply it is present). * add_or_reconnect: do the same but add the esxi if absent. Also: * tag the cluster_name as required (because it is). * tag esxi_username and esxi_password as not required because they aren't when the esxi isn't added. * vmware_host: add + prepare/document integration tests vmware_host module Add integration test for the add part of "add_or_reconnect" state. Prepare and document integration tests for the reconnect part of "add_or_reconnect" state and "reconnect" and "absent" states. Currently we can't test those states as ReconnectHost_Task (for "reconnect") and EnterMaintenanceMode_Task (for "absent") aren't implemented yet in vcsim (from vmware/govmomi)
This commit is contained in:
parent
d08179593f
commit
29bed12cdd
2 changed files with 208 additions and 24 deletions
|
@ -17,11 +17,12 @@ DOCUMENTATION = r'''
|
||||||
module: vmware_host
|
module: vmware_host
|
||||||
short_description: Add/remove ESXi host to/from vCenter
|
short_description: Add/remove ESXi host to/from vCenter
|
||||||
description:
|
description:
|
||||||
- This module can be used to add/remove an ESXi host to/from vCenter.
|
- This module can be used to add/remove/reconnect an ESXi host to/from vCenter.
|
||||||
version_added: '2.0'
|
version_added: '2.0'
|
||||||
author:
|
author:
|
||||||
- Joseph Callen (@jcpowermac)
|
- Joseph Callen (@jcpowermac)
|
||||||
- Russell Teague (@mtnbikenc)
|
- Russell Teague (@mtnbikenc)
|
||||||
|
- Maxime de Roucy (@tchernomax)
|
||||||
notes:
|
notes:
|
||||||
- Tested on vSphere 5.5
|
- Tested on vSphere 5.5
|
||||||
requirements:
|
requirements:
|
||||||
|
@ -35,6 +36,7 @@ options:
|
||||||
cluster_name:
|
cluster_name:
|
||||||
description:
|
description:
|
||||||
- Name of the cluster to add the host.
|
- Name of the cluster to add the host.
|
||||||
|
- Required from version 2.5.
|
||||||
required: yes
|
required: yes
|
||||||
esxi_hostname:
|
esxi_hostname:
|
||||||
description:
|
description:
|
||||||
|
@ -43,16 +45,29 @@ options:
|
||||||
esxi_username:
|
esxi_username:
|
||||||
description:
|
description:
|
||||||
- ESXi username.
|
- ESXi username.
|
||||||
required: yes
|
- Required for adding a host.
|
||||||
|
- Optional for reconnect.
|
||||||
|
- Unused for removing.
|
||||||
|
- No longer required from version 2.5.
|
||||||
esxi_password:
|
esxi_password:
|
||||||
description:
|
description:
|
||||||
- ESXi password.
|
- ESXi password.
|
||||||
required: yes
|
- Required for adding a host.
|
||||||
|
- Optional for reconnect.
|
||||||
|
- Unused for removing.
|
||||||
|
- No longer required from version 2.5.
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
- Add or remove the host.
|
- "present: add the host if it's absent else do nothing."
|
||||||
choices: [absent, present]
|
- "absent: remove the host if it's present else do nothing."
|
||||||
|
- "add_or_reconnect: add the host if it's absent else reconnect it."
|
||||||
|
- "reconnect: reconnect the host if it's present else fail."
|
||||||
default: present
|
default: present
|
||||||
|
choices:
|
||||||
|
- present
|
||||||
|
- absent
|
||||||
|
- add_or_reconnect
|
||||||
|
- reconnect
|
||||||
extends_documentation_fragment: vmware.documentation
|
extends_documentation_fragment: vmware.documentation
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -69,6 +84,30 @@ EXAMPLES = r'''
|
||||||
esxi_password: '{{ esxi_password }}'
|
esxi_password: '{{ esxi_password }}'
|
||||||
state: present
|
state: present
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Reconnect ESXi Host (with username/password set)
|
||||||
|
vmware_host:
|
||||||
|
hostname: '{{ vcenter_hostname }}'
|
||||||
|
username: '{{ vcenter_username }}'
|
||||||
|
password: '{{ vcenter_password }}'
|
||||||
|
datacenter_name: datacenter_name
|
||||||
|
cluster_name: cluster_name
|
||||||
|
esxi_hostname: '{{ esxi_hostname }}'
|
||||||
|
esxi_username: '{{ esxi_username }}'
|
||||||
|
esxi_password: '{{ esxi_password }}'
|
||||||
|
state: reconnect
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Reconnect ESXi Host (with default username/password)
|
||||||
|
vmware_host:
|
||||||
|
hostname: '{{ vcenter_hostname }}'
|
||||||
|
username: '{{ vcenter_username }}'
|
||||||
|
password: '{{ vcenter_password }}'
|
||||||
|
datacenter_name: datacenter_name
|
||||||
|
cluster_name: cluster_name
|
||||||
|
esxi_hostname: '{{ esxi_hostname }}'
|
||||||
|
state: reconnect
|
||||||
|
delegate_to: localhost
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RETURN = r'''
|
RETURN = r'''
|
||||||
|
@ -99,7 +138,6 @@ class VMwareHost(object):
|
||||||
self.esxi_username = module.params['esxi_username']
|
self.esxi_username = module.params['esxi_username']
|
||||||
self.esxi_password = module.params['esxi_password']
|
self.esxi_password = module.params['esxi_password']
|
||||||
self.state = module.params['state']
|
self.state = module.params['state']
|
||||||
self.dc = None
|
|
||||||
self.cluster = None
|
self.cluster = None
|
||||||
self.host = None
|
self.host = None
|
||||||
self.content = connect_to_api(module)
|
self.content = connect_to_api(module)
|
||||||
|
@ -115,6 +153,13 @@ class VMwareHost(object):
|
||||||
'present': {
|
'present': {
|
||||||
'present': self.state_exit_unchanged,
|
'present': self.state_exit_unchanged,
|
||||||
'absent': self.state_add_host,
|
'absent': self.state_add_host,
|
||||||
|
},
|
||||||
|
'add_or_reconnect': {
|
||||||
|
'present': self.state_reconnect_host,
|
||||||
|
'absent': self.state_add_host,
|
||||||
|
},
|
||||||
|
'reconnect': {
|
||||||
|
'present': self.state_reconnect_host,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +173,10 @@ class VMwareHost(object):
|
||||||
self.module.fail_json(msg=str(e))
|
self.module.fail_json(msg=str(e))
|
||||||
|
|
||||||
def add_host_to_vcenter(self):
|
def add_host_to_vcenter(self):
|
||||||
|
|
||||||
|
if self.esxi_username is None or self.esxi_password is None:
|
||||||
|
self.module.fail_json(msg='esxi_username and esxi_password are required to add a host')
|
||||||
|
|
||||||
host_connect_spec = vim.host.ConnectSpec()
|
host_connect_spec = vim.host.ConnectSpec()
|
||||||
host_connect_spec.hostName = self.esxi_hostname
|
host_connect_spec.hostName = self.esxi_hostname
|
||||||
host_connect_spec.userName = self.esxi_username
|
host_connect_spec.userName = self.esxi_username
|
||||||
|
@ -156,6 +205,32 @@ class VMwareHost(object):
|
||||||
success, result = wait_for_task(task)
|
success, result = wait_for_task(task)
|
||||||
return success, result
|
return success, result
|
||||||
|
|
||||||
|
def reconnect_host_to_vcenter(self):
|
||||||
|
reconnecthost_args = {}
|
||||||
|
reconnecthost_args['reconnectSpec'] = vim.HostSystem.ReconnectSpec()
|
||||||
|
reconnecthost_args['reconnectSpec'].syncState = True
|
||||||
|
|
||||||
|
if self.esxi_username is not None or self.esxi_password is not None:
|
||||||
|
reconnecthost_args['cnxSpec'] = vim.host.ConnectSpec()
|
||||||
|
reconnecthost_args['cnxSpec'].hostName = self.esxi_hostname
|
||||||
|
reconnecthost_args['cnxSpec'].userName = self.esxi_username
|
||||||
|
reconnecthost_args['cnxSpec'].password = self.esxi_password
|
||||||
|
reconnecthost_args['cnxSpec'].force = True
|
||||||
|
reconnecthost_args['cnxSpec'].sslThumbprint = ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
task = self.host.ReconnectHost_Task(**reconnecthost_args)
|
||||||
|
success, result = wait_for_task(task)
|
||||||
|
return success, result
|
||||||
|
except TaskError as add_task_error:
|
||||||
|
# See add_host_to_vcenter
|
||||||
|
ssl_verify_fault = add_task_error.args[0]
|
||||||
|
reconnecthost_args['cnxSpec'].sslThumbprint = ssl_verify_fault.thumbprint
|
||||||
|
|
||||||
|
task = self.host.ReconnectHost_Task(**reconnecthost_args)
|
||||||
|
success, result = wait_for_task(task)
|
||||||
|
return success, result
|
||||||
|
|
||||||
def state_exit_unchanged(self):
|
def state_exit_unchanged(self):
|
||||||
self.module.exit_json(changed=False)
|
self.module.exit_json(changed=False)
|
||||||
|
|
||||||
|
@ -185,6 +260,14 @@ class VMwareHost(object):
|
||||||
changed, result = self.add_host_to_vcenter()
|
changed, result = self.add_host_to_vcenter()
|
||||||
self.module.exit_json(changed=changed, result=str(result))
|
self.module.exit_json(changed=changed, result=str(result))
|
||||||
|
|
||||||
|
def state_reconnect_host(self):
|
||||||
|
changed = True
|
||||||
|
result = None
|
||||||
|
|
||||||
|
if not self.module.check_mode:
|
||||||
|
changed, result = self.reconnect_host_to_vcenter()
|
||||||
|
self.module.exit_json(changed=changed, result=str(result))
|
||||||
|
|
||||||
def check_host_state(self):
|
def check_host_state(self):
|
||||||
self.host, self.cluster = find_host_by_cluster_datacenter(self.module, self.content, self.datacenter_name,
|
self.host, self.cluster = find_host_by_cluster_datacenter(self.module, self.content, self.datacenter_name,
|
||||||
self.cluster_name, self.esxi_hostname)
|
self.cluster_name, self.esxi_hostname)
|
||||||
|
@ -199,17 +282,20 @@ def main():
|
||||||
argument_spec = vmware_argument_spec()
|
argument_spec = vmware_argument_spec()
|
||||||
argument_spec.update(
|
argument_spec.update(
|
||||||
datacenter_name=dict(type='str', required=True),
|
datacenter_name=dict(type='str', required=True),
|
||||||
cluster_name=dict(type='str'),
|
cluster_name=dict(type='str', required=True),
|
||||||
esxi_hostname=dict(type='str', required=True),
|
esxi_hostname=dict(type='str', required=True),
|
||||||
esxi_username=dict(type='str', required=True),
|
esxi_username=dict(type='str', required=False),
|
||||||
esxi_password=dict(type='str', required=True, no_log=True),
|
esxi_password=dict(type='str', required=False, no_log=True),
|
||||||
state=dict(type='str', default='present', choices=['absent', 'present'])
|
state=dict(default='present',
|
||||||
)
|
choices=['present', 'absent', 'add_or_reconnect', 'reconnect'],
|
||||||
|
type='str'))
|
||||||
|
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=argument_spec,
|
argument_spec=argument_spec,
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
required_if=[
|
||||||
|
['state', 'present', ['esxi_username', 'esxi_password']],
|
||||||
|
['state', 'add_or_reconnect', ['esxi_username', 'esxi_password']]])
|
||||||
|
|
||||||
if not HAS_PYVMOMI:
|
if not HAS_PYVMOMI:
|
||||||
module.fail_json(msg='pyvmomi is required for this module')
|
module.fail_json(msg='pyvmomi is required for this module')
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
|
|
||||||
- debug: var=dc1
|
- debug: var=dc1
|
||||||
|
|
||||||
|
|
||||||
- name: get a list of Cluster from vcsim
|
- name: get a list of Cluster from vcsim
|
||||||
uri:
|
uri:
|
||||||
url: http://{{ vcsim }}:5000/govc_find?filter=CCR
|
url: http://{{ vcsim }}:5000/govc_find?filter=CCR
|
||||||
|
@ -61,7 +60,7 @@
|
||||||
|
|
||||||
- debug: var=ccr1
|
- debug: var=ccr1
|
||||||
|
|
||||||
# Testcase 0001: Add Host
|
# Testcase: Add Host
|
||||||
- name: add host
|
- name: add host
|
||||||
vmware_host:
|
vmware_host:
|
||||||
hostname: "{{ vcsim }}"
|
hostname: "{{ vcsim }}"
|
||||||
|
@ -74,23 +73,20 @@
|
||||||
datacenter_name: "{{ dc1 }}"
|
datacenter_name: "{{ dc1 }}"
|
||||||
cluster_name: "{{ ccr1 }}"
|
cluster_name: "{{ ccr1 }}"
|
||||||
state: present
|
state: present
|
||||||
register: host_system_result_0001
|
register: add_host_result
|
||||||
|
|
||||||
- name: get a list of host system from vcsim after adding host system
|
- name: get a list of host system from vcsim after adding host system
|
||||||
uri:
|
uri:
|
||||||
url: http://{{ vcsim }}:5000/govc_find?filter=H
|
url: http://{{ vcsim }}:5000/govc_find?filter=H
|
||||||
register: new_host_list
|
register: host_list
|
||||||
|
|
||||||
- set_fact:
|
|
||||||
new_host: "{% for host in new_host_list.json %} {{ True if (host | basename) == 'test_host_system_0001' else False }} {% endfor %}"
|
|
||||||
|
|
||||||
- name: ensure host system is present
|
- name: ensure host system is present
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- host_system_result_0001.changed == true
|
- add_host_result | changed
|
||||||
- "'True' in new_host"
|
- "{% for host in host_list.json if ((host | basename) == 'test_host_system_0001') -%} True {%- else -%} False {%- endfor %}"
|
||||||
|
|
||||||
# Testcase 0002: Add Host again
|
# Testcase: Add Host again
|
||||||
- name: add host again
|
- name: add host again
|
||||||
vmware_host:
|
vmware_host:
|
||||||
hostname: "{{ vcsim }}"
|
hostname: "{{ vcsim }}"
|
||||||
|
@ -103,9 +99,111 @@
|
||||||
datacenter_name: "{{ dc1 }}"
|
datacenter_name: "{{ dc1 }}"
|
||||||
cluster_name: "{{ ccr1 }}"
|
cluster_name: "{{ ccr1 }}"
|
||||||
state: present
|
state: present
|
||||||
register: host_system_result_0002
|
register: readd_host_result
|
||||||
|
|
||||||
|
- name: ensure precend task didn't changed anything
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not (readd_host_result|changed)
|
||||||
|
|
||||||
|
# Testcase: Add Host via add_or_reconnect state
|
||||||
|
- name: add host via add_or_reconnect
|
||||||
|
vmware_host:
|
||||||
|
hostname: "{{ vcsim }}"
|
||||||
|
username: "{{ vcsim_instance.json.username }}"
|
||||||
|
password: "{{ vcsim_instance.json.password }}"
|
||||||
|
validate_certs: no
|
||||||
|
esxi_hostname: test_host_system_0002
|
||||||
|
esxi_username: "{{ vcsim_instance.json.username }}"
|
||||||
|
esxi_password: "{{ vcsim_instance.json.password }}"
|
||||||
|
datacenter_name: "{{ dc1 }}"
|
||||||
|
cluster_name: "{{ ccr1 }}"
|
||||||
|
state: add_or_reconnect
|
||||||
|
register: add_or_reconnect_host_result
|
||||||
|
|
||||||
|
- name: get a list of host system from vcsim after adding host system
|
||||||
|
uri:
|
||||||
|
url: http://{{ vcsim }}:5000/govc_find?filter=H
|
||||||
|
register: host_list
|
||||||
|
|
||||||
- name: ensure host system is present
|
- name: ensure host system is present
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- host_system_result_0002.changed == false
|
- add_or_reconnect_host_result | changed
|
||||||
|
- "{% for host in host_list.json if ((host | basename) == 'test_host_system_0002') -%} True {%- else -%} False {%- endfor %}"
|
||||||
|
|
||||||
|
## Testcase: Reconnect Host
|
||||||
|
#
|
||||||
|
# ReconnectHost_Task need to be implemented in vcsim for this test to work
|
||||||
|
# https://github.com/vmware/govmomi/tree/master/vcsim#supported-methods
|
||||||
|
#
|
||||||
|
#- name: reconnect host
|
||||||
|
# vmware_host:
|
||||||
|
# hostname: "{{ vcsim }}"
|
||||||
|
# username: "{{ vcsim_instance.json.username }}"
|
||||||
|
# password: "{{ vcsim_instance.json.password }}"
|
||||||
|
# validate_certs: no
|
||||||
|
# esxi_hostname: test_host_system_0001
|
||||||
|
# datacenter_name: "{{ dc1 }}"
|
||||||
|
# cluster_name: "{{ ccr1 }}"
|
||||||
|
# state: reconnect
|
||||||
|
# register: reconnect_host_result
|
||||||
|
#
|
||||||
|
#- name: ensure host system has been reconnected
|
||||||
|
# assert:
|
||||||
|
# that:
|
||||||
|
# - reconnect_host_result | changed
|
||||||
|
# # it would be a good idea to check the events on the host to see the reconnect
|
||||||
|
# # https://github.com/vmware/govmomi/blob/master/govc/USAGE.md#events
|
||||||
|
# # "govc events ..." need to be callable from
|
||||||
|
# # test/utils/docker/vcenter-simulator/flask_control.py
|
||||||
|
|
||||||
|
## Testcase: Remove Host
|
||||||
|
#
|
||||||
|
# EnterMaintenanceMode_Task need to be implemented in vcsim for this test to work
|
||||||
|
# https://github.com/vmware/govmomi/tree/master/vcsim#supported-methods
|
||||||
|
#
|
||||||
|
#- name: remove host
|
||||||
|
# vmware_host:
|
||||||
|
# hostname: "{{ vcsim }}"
|
||||||
|
# username: "{{ vcsim_instance.json.username }}"
|
||||||
|
# password: "{{ vcsim_instance.json.password }}"
|
||||||
|
# validate_certs: no
|
||||||
|
# esxi_hostname: test_host_system_0001
|
||||||
|
# datacenter_name: "{{ dc1 }}"
|
||||||
|
# cluster_name: "{{ ccr1 }}"
|
||||||
|
# state: absent
|
||||||
|
# register: remove_host_result
|
||||||
|
#
|
||||||
|
#- name: get a list of host system from vcsim after removing host system
|
||||||
|
# uri:
|
||||||
|
# url: http://{{ vcsim }}:5000/govc_find?filter=H
|
||||||
|
# register: host_list
|
||||||
|
#
|
||||||
|
#- name: ensure host system is absent
|
||||||
|
# assert:
|
||||||
|
# that:
|
||||||
|
# - remove_host_result | changed
|
||||||
|
# - "{% for host in host_list.json if ((host | basename) == 'test_host_system_0001') -%} False {%- else -%} True {%- endfor %}"
|
||||||
|
|
||||||
|
## Testcase: Remove Host again
|
||||||
|
#
|
||||||
|
# EnterMaintenanceMode_Task need to be implemented in vcsim for this test to work
|
||||||
|
# https://github.com/vmware/govmomi/tree/master/vcsim#supported-methods
|
||||||
|
#
|
||||||
|
#- name: remove host again
|
||||||
|
# vmware_host:
|
||||||
|
# hostname: "{{ vcsim }}"
|
||||||
|
# username: "{{ vcsim_instance.json.username }}"
|
||||||
|
# password: "{{ vcsim_instance.json.password }}"
|
||||||
|
# validate_certs: no
|
||||||
|
# esxi_hostname: test_host_system_0001
|
||||||
|
# datacenter_name: "{{ dc1 }}"
|
||||||
|
# cluster_name: "{{ ccr1 }}"
|
||||||
|
# state: absent
|
||||||
|
# register: reremove_host_result
|
||||||
|
#
|
||||||
|
#- name: ensure precend task didn't changed anything
|
||||||
|
# assert:
|
||||||
|
# that:
|
||||||
|
# - not (reremove_host_result|changed)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue