From 0ea19499ae1eaf88eeba3de23d51c385082d3fab Mon Sep 17 00:00:00 2001 From: Joseph Callen Date: Mon, 24 Aug 2015 13:10:21 -0400 Subject: [PATCH] New VMware Module to support adding a cluster to vCenter --- .../extras/cloud/vmware/vmware_cluster.py | 286 ++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 lib/ansible/modules/extras/cloud/vmware/vmware_cluster.py diff --git a/lib/ansible/modules/extras/cloud/vmware/vmware_cluster.py b/lib/ansible/modules/extras/cloud/vmware/vmware_cluster.py new file mode 100644 index 00000000000..d803878b584 --- /dev/null +++ b/lib/ansible/modules/extras/cloud/vmware/vmware_cluster.py @@ -0,0 +1,286 @@ +#!/bin/python +# -*- coding: utf-8 -*- + +# (c) 2015, Joseph Callen +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +DOCUMENTATION = ''' +--- +module: vmware_cluster +short_description: Create VMware vSphere Cluster +description: + - Create VMware vSphere Cluster +version_added: 2.0 +author: Joseph Callen +notes: +requirements: + - Tested on ESXi 5.5 + - PyVmomi installed +options: + hostname: + description: + - The hostname or IP address of the vSphere vCenter + required: True + version_added: 2.0 + username: + description: + - The username of the vSphere vCenter + required: True + aliases: ['user', 'admin'] + version_added: 2.0 + password: + description: + - The password of the vSphere vCenter + required: True + aliases: ['pass', 'pwd'] + version_added: 2.0 + datacenter_name: + description: + - The name of the datacenter the cluster will be created in. + required: True + version_added: 2.0 + cluster_name: + description: + - The name of the cluster that will be created + required: True + version_added: 2.0 + enable_ha: + description: + - If set to True will enable HA when the cluster is created. + required: False + default: False + version_added: 2.0 + enable_drs: + description: + - If set to True will enable DRS when the cluster is created. + required: False + default: False + version_added: 2.0 + enable_vsan: + description: + - If set to True will enable vSAN when the cluster is created. + required: False + default: False + version_added: 2.0 +''' + +EXAMPLES = ''' +# Example vmware_cluster command from Ansible Playbooks +- name: Create Cluster + local_action: > + vmware_cluster + hostname="{{ ansible_ssh_host }}" username=root password=vmware + datacenter_name="datacenter" + cluster_name="cluster" + enable_ha=True + enable_drs=True + enable_vsan=True +''' + +try: + from pyVmomi import vim, vmodl + HAS_PYVMOMI = True +except ImportError: + HAS_PYVMOMI = False + + +def configure_ha(enable_ha): + das_config = vim.cluster.DasConfigInfo() + das_config.enabled = enable_ha + das_config.admissionControlPolicy = vim.cluster.FailoverLevelAdmissionControlPolicy() + das_config.admissionControlPolicy.failoverLevel = 2 + return das_config + + +def configure_drs(enable_drs): + drs_config = vim.cluster.DrsConfigInfo() + drs_config.enabled = enable_drs + # Set to partially automated + drs_config.vmotionRate = 3 + return drs_config + + +def configure_vsan(enable_vsan): + vsan_config = vim.vsan.cluster.ConfigInfo() + vsan_config.enabled = enable_vsan + vsan_config.defaultConfig = vim.vsan.cluster.ConfigInfo.HostDefaultInfo() + vsan_config.defaultConfig.autoClaimStorage = False + return vsan_config + + +def state_create_cluster(module): + + enable_ha = module.params['enable_ha'] + enable_drs = module.params['enable_drs'] + enable_vsan = module.params['enable_vsan'] + cluster_name = module.params['cluster_name'] + datacenter = module.params['datacenter'] + + try: + cluster_config_spec = vim.cluster.ConfigSpecEx() + cluster_config_spec.dasConfig = configure_ha(enable_ha) + cluster_config_spec.drsConfig = configure_drs(enable_drs) + if enable_vsan: + cluster_config_spec.vsanConfig = configure_vsan(enable_vsan) + if not module.check_mode: + datacenter.hostFolder.CreateClusterEx(cluster_name, cluster_config_spec) + module.exit_json(changed=True) + except vim.fault.DuplicateName: + module.fail_json(msg="A cluster with the name %s already exists" % cluster_name) + except vmodl.fault.InvalidArgument: + module.fail_json(msg="Cluster configuration specification parameter is invalid") + except vim.fault.InvalidName: + module.fail_json(msg="%s is an invalid name for a cluster" % cluster_name) + except vmodl.fault.NotSupported: + # This should never happen + module.fail_json(msg="Trying to create a cluster on an incorrect folder object") + except vmodl.RuntimeFault as runtime_fault: + module.fail_json(msg=runtime_fault.msg) + except vmodl.MethodFault as method_fault: + # This should never happen either + module.fail_json(msg=method_fault.msg) + + +def state_destroy_cluster(module): + cluster = module.params['cluster'] + changed = True + result = None + + try: + if not module.check_mode: + task = cluster.Destroy_Task() + changed, result = wait_for_task(task) + module.exit_json(changed=changed, result=result) + except vim.fault.VimFault as vim_fault: + module.fail_json(msg=vim_fault.msg) + except vmodl.RuntimeFault as runtime_fault: + module.fail_json(msg=runtime_fault.msg) + except vmodl.MethodFault as method_fault: + module.fail_json(msg=method_fault.msg) + + +def state_exit_unchanged(module): + module.exit_json(changed=False) + + +def state_update_cluster(module): + + cluster_config_spec = vim.cluster.ConfigSpecEx() + cluster = module.params['cluster'] + enable_ha = module.params['enable_ha'] + enable_drs = module.params['enable_drs'] + enable_vsan = module.params['enable_vsan'] + changed = True + result = None + + if cluster.configurationEx.dasConfig.enabled != enable_ha: + cluster_config_spec.dasConfig = configure_ha(enable_ha) + if cluster.configurationEx.drsConfig.enabled != enable_drs: + cluster_config_spec.drsConfig = configure_drs(enable_drs) + if cluster.configurationEx.vsanConfigInfo.enabled != enable_vsan: + cluster_config_spec.vsanConfig = configure_vsan(enable_vsan) + + try: + if not module.check_mode: + task = cluster.ReconfigureComputeResource_Task(cluster_config_spec, True) + changed, result = wait_for_task(task) + module.exit_json(changed=changed, result=result) + except vmodl.RuntimeFault as runtime_fault: + module.fail_json(msg=runtime_fault.msg) + except vmodl.MethodFault as method_fault: + module.fail_json(msg=method_fault.msg) + except TaskError as task_e: + module.fail_json(msg=str(task_e)) + + +def check_cluster_configuration(module): + datacenter_name = module.params['datacenter_name'] + cluster_name = module.params['cluster_name'] + + try: + content = connect_to_api(module) + datacenter = find_datacenter_by_name(content, datacenter_name) + if datacenter is None: + module.fail_json(msg="Datacenter %s does not exist, " + "please create first with Ansible Module vmware_datacenter or manually." + % datacenter_name) + cluster = find_cluster_by_name_datacenter(datacenter, cluster_name) + + module.params['content'] = content + module.params['datacenter'] = datacenter + + if cluster is None: + return 'absent' + else: + module.params['cluster'] = cluster + + desired_state = (module.params['enable_ha'], + module.params['enable_drs'], + module.params['enable_vsan']) + + current_state = (cluster.configurationEx.dasConfig.enabled, + cluster.configurationEx.drsConfig.enabled, + cluster.configurationEx.vsanConfigInfo.enabled) + + if cmp(desired_state, current_state) != 0: + return 'update' + else: + return 'present' + except vmodl.RuntimeFault as runtime_fault: + module.fail_json(msg=runtime_fault.msg) + except vmodl.MethodFault as method_fault: + module.fail_json(msg=method_fault.msg) + + +def main(): + + argument_spec = vmware_argument_spec() + argument_spec.update(dict(datacenter_name=dict(required=True, type='str'), + cluster_name=dict(required=True, type='str'), + enable_ha=dict(default=False, required=False, type='bool'), + enable_drs=dict(default=False, required=False, type='bool'), + enable_vsan=dict(default=False, required=False, type='bool'), + state=dict(default='present', choices=['present', 'absent'], type='str'))) + + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + + if not HAS_PYVMOMI: + module.fail_json(msg='pyvmomi is required for this module') + + cluster_states = { + 'absent': { + 'present': state_destroy_cluster, + 'absent': state_exit_unchanged, + }, + 'present': { + 'update': state_update_cluster, + 'present': state_exit_unchanged, + 'absent': state_create_cluster, + } + } + desired_state = module.params['state'] + current_state = check_cluster_configuration(module) + + # Based on the desired_state and the current_state call + # the appropriate method from the dictionary + cluster_states[desired_state][current_state](module) + +from ansible.module_utils.vmware import * +from ansible.module_utils.basic import * + +if __name__ == '__main__': + main()