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:
tchernomax 2017-12-13 17:13:12 +01:00 committed by Sam Doran
parent d08179593f
commit 29bed12cdd
2 changed files with 208 additions and 24 deletions

View file

@ -17,11 +17,12 @@ DOCUMENTATION = r'''
module: vmware_host
short_description: Add/remove ESXi host to/from vCenter
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'
author:
- Joseph Callen (@jcpowermac)
- Russell Teague (@mtnbikenc)
- Maxime de Roucy (@tchernomax)
notes:
- Tested on vSphere 5.5
requirements:
@ -35,6 +36,7 @@ options:
cluster_name:
description:
- Name of the cluster to add the host.
- Required from version 2.5.
required: yes
esxi_hostname:
description:
@ -43,16 +45,29 @@ options:
esxi_username:
description:
- ESXi username.
required: yes
- Required for adding a host.
- Optional for reconnect.
- Unused for removing.
- No longer required from version 2.5.
esxi_password:
description:
- ESXi password.
required: yes
- Required for adding a host.
- Optional for reconnect.
- Unused for removing.
- No longer required from version 2.5.
state:
description:
- Add or remove the host.
choices: [absent, present]
- "present: add the host if it's absent else do nothing."
- "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
choices:
- present
- absent
- add_or_reconnect
- reconnect
extends_documentation_fragment: vmware.documentation
'''
@ -69,6 +84,30 @@ EXAMPLES = r'''
esxi_password: '{{ esxi_password }}'
state: present
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'''
@ -99,7 +138,6 @@ class VMwareHost(object):
self.esxi_username = module.params['esxi_username']
self.esxi_password = module.params['esxi_password']
self.state = module.params['state']
self.dc = None
self.cluster = None
self.host = None
self.content = connect_to_api(module)
@ -115,6 +153,13 @@ class VMwareHost(object):
'present': {
'present': self.state_exit_unchanged,
'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))
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.hostName = self.esxi_hostname
host_connect_spec.userName = self.esxi_username
@ -156,6 +205,32 @@ class VMwareHost(object):
success, result = wait_for_task(task)
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):
self.module.exit_json(changed=False)
@ -185,6 +260,14 @@ class VMwareHost(object):
changed, result = self.add_host_to_vcenter()
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):
self.host, self.cluster = find_host_by_cluster_datacenter(self.module, self.content, self.datacenter_name,
self.cluster_name, self.esxi_hostname)
@ -199,17 +282,20 @@ def main():
argument_spec = vmware_argument_spec()
argument_spec.update(
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_username=dict(type='str', required=True),
esxi_password=dict(type='str', required=True, no_log=True),
state=dict(type='str', default='present', choices=['absent', 'present'])
)
esxi_username=dict(type='str', required=False),
esxi_password=dict(type='str', required=False, no_log=True),
state=dict(default='present',
choices=['present', 'absent', 'add_or_reconnect', 'reconnect'],
type='str'))
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)
required_if=[
['state', 'present', ['esxi_username', 'esxi_password']],
['state', 'add_or_reconnect', ['esxi_username', 'esxi_password']]])
if not HAS_PYVMOMI:
module.fail_json(msg='pyvmomi is required for this module')

View file

@ -49,7 +49,6 @@
- debug: var=dc1
- name: get a list of Cluster from vcsim
uri:
url: http://{{ vcsim }}:5000/govc_find?filter=CCR
@ -61,7 +60,7 @@
- debug: var=ccr1
# Testcase 0001: Add Host
# Testcase: Add Host
- name: add host
vmware_host:
hostname: "{{ vcsim }}"
@ -74,23 +73,20 @@
datacenter_name: "{{ dc1 }}"
cluster_name: "{{ ccr1 }}"
state: present
register: host_system_result_0001
register: add_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: new_host_list
- set_fact:
new_host: "{% for host in new_host_list.json %} {{ True if (host | basename) == 'test_host_system_0001' else False }} {% endfor %}"
register: host_list
- name: ensure host system is present
assert:
that:
- host_system_result_0001.changed == true
- "'True' in new_host"
- add_host_result | changed
- "{% 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
vmware_host:
hostname: "{{ vcsim }}"
@ -103,9 +99,111 @@
datacenter_name: "{{ dc1 }}"
cluster_name: "{{ ccr1 }}"
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
assert:
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)