diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py index 411a71bdaf..5501e9a17b 100644 --- a/lib/ansible/module_utils/vmware.py +++ b/lib/ansible/module_utils/vmware.py @@ -1088,6 +1088,42 @@ class PyVmomi(object): """ return find_hostsystem_by_name(self.content, hostname=host_name) + def get_all_host_objs(self, cluster_name=None, esxi_host_name=None): + """ + Function to get all host system managed object + + Args: + cluster_name: Name of Cluster + esxi_host_name: Name of ESXi server + + Returns: A list of all host system managed objects, else empty list + + """ + host_obj_list = [] + if not self.is_vcenter(): + hosts = get_all_objs(self.content, [vim.HostSystem]).keys() + if hosts: + host_obj_list.append(hosts[0]) + else: + if cluster_name: + cluster_obj = self.find_cluster_by_name(cluster_name=cluster_name) + if cluster_obj: + host_obj_list = [host for host in cluster_obj.host] + else: + self.module.fail_json(changed=False, msg="Cluster '%s' not found" % cluster_name) + elif esxi_host_name: + if isinstance(esxi_host_name, str): + esxi_host_name = [esxi_host_name] + + for host in esxi_host_name: + esxi_host_obj = self.find_hostsystem_by_name(host_name=host) + if esxi_host_obj: + host_obj_list = [esxi_host_obj] + else: + self.module.fail_json(changed=False, msg="ESXi '%s' not found" % host) + + return host_obj_list + # Network related functions @staticmethod def find_host_portgroup_by_name(host, portgroup_name): @@ -1118,35 +1154,18 @@ class PyVmomi(object): pgs_list.append(pg) return pgs_list - def get_all_host_objs(self, cluster_name=None, esxi_host_name=None): + # Datacenter + def find_datacenter_by_name(self, datacenter_name): """ - Function to get all host system managed object + Function to get datacenter managed object by name Args: - cluster_name: Name of Cluster - esxi_host_name: Name of ESXi server + datacenter_name: Name of datacenter - Returns: A list of all host system managed objects, else empty list + Returns: datacenter managed object if found else None """ - host_obj_list = [] - if not self.is_vcenter(): - host_obj_list.append(get_all_objs(self.content, [vim.HostSystem])[0]) - else: - if cluster_name: - cluster_obj = self.find_cluster_by_name(cluster_name=cluster_name) - if cluster_obj: - host_obj_list = [host for host in cluster_obj.host] - else: - self.module.fail_json(changed=False, msg="Cluster '%s' not found" % cluster_name) - elif esxi_host_name: - esxi_host_obj = self.find_hostsystem_by_name(host_name=esxi_host_name) - if esxi_host_obj: - host_obj_list = [esxi_host_obj] - else: - self.module.fail_json(changed=False, msg="ESXi '%s' not found" % esxi_host_name) - - return host_obj_list + return find_datacenter_by_name(self.content, datacenter_name=datacenter_name) def find_datastore_by_name(self, datastore_name): """ @@ -1158,3 +1177,19 @@ class PyVmomi(object): """ return find_datastore_by_name(self.content, datastore_name=datastore_name) + + # Datastore cluster + def find_datastore_cluster_by_name(self, datastore_cluster_name): + """ + Function to get datastore cluster managed object by name + Args: + datastore_cluster: Name of datastore cluster + + Returns: Datastore cluster managed object if found else None + + """ + data_store_clusters = get_all_objs(self.content, [vim.StoragePod]) + for dsc in data_store_clusters: + if dsc.name == datastore_cluster_name: + return dsc + return None diff --git a/lib/ansible/modules/cloud/vmware/vmware_datastore_cluster.py b/lib/ansible/modules/cloud/vmware/vmware_datastore_cluster.py new file mode 100644 index 0000000000..091f2b1dd3 --- /dev/null +++ b/lib/ansible/modules/cloud/vmware/vmware_datastore_cluster.py @@ -0,0 +1,159 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Ansible Project +# Copyright (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + + +DOCUMENTATION = ''' +--- +module: vmware_datastore_cluster +short_description: Manage VMware vSphere datastore clusters +description: + - This module can be used to add and delete datastore cluster in given VMware environment. + - All parameters and VMware object values are case sensitive. +version_added: 2.6 +author: +- Abhijeet Kasurde (@akasurde) +notes: + - Tested on vSphere 6.0, 6.5 +requirements: + - "python >= 2.6" + - PyVmomi +options: + datacenter_name: + description: + - The name of the datacenter. + required: True + datastore_cluster_name: + description: + - The name of the datastore cluster. + required: True + state: + description: + - If the datastore cluster should be present or absent. + choices: [ present, absent ] + default: present +extends_documentation_fragment: vmware.documentation +''' + +EXAMPLES = ''' +- name: Create datastore cluster + vmware_datastore_cluster: + hostname: vCenter + username: root + password: vmware + datacenter_name: "datacenter" + datastore_cluster_name: datacluster0 + state: present + + +- name: Delete datastore cluster + vmware_datastore_cluster: + hostname: vCenter + username: root + password: vmware + datacenter_name: "datacenter" + datastore_cluster_name: datacluster0 + state: absent +''' + +RETURN = """ +result: + description: information about datastore cluster operation + returned: always + type: string + sample: "Datastore cluster 'DSC2' created successfully." +""" + +try: + from pyVmomi import vim, vmodl +except ImportError: + pass + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.vmware import PyVmomi, vmware_argument_spec, wait_for_task +from ansible.module_utils._text import to_native + + +class VMwareDatastoreClusterManager(PyVmomi): + def __init__(self, module): + super(VMwareDatastoreClusterManager, self).__init__(module) + datacenter_name = self.params.get('datacenter_name') + self.datacenter_obj = self.find_datacenter_by_name(datacenter_name) + if not self.datacenter_obj: + self.module.fail_json(msg="Failed to find datacenter '%s' required" + " for managing datastore cluster." % datacenter_name) + self.datastore_cluster_name = self.params.get('datastore_cluster_name') + self.datastore_cluster_obj = self.find_datastore_cluster_by_name(self.datastore_cluster_name) + + def ensure(self): + """ + Function to manage internal state of datastore cluster + + """ + results = dict(changed=False, result='') + state = self.module.params.get('state') + + if self.datastore_cluster_obj: + if state == 'present': + results['result'] = "Datastore cluster '%s' already available." % self.datastore_cluster_name + elif state == 'absent': + # Delete datastore cluster + if not self.module.check_mode: + task = self.datastore_cluster_obj.Destroy_Task() + changed, result = wait_for_task(task) + else: + changed = True + if changed: + results['result'] = "Datastore cluster '%s' deleted successfully." % self.datastore_cluster_name + results['changed'] = changed + else: + self.module.fail_json(msg="Failed to delete datastore cluster '%s'." % self.datastore_cluster_name) + else: + if state == 'present': + # Create datastore cluster + if not self.module.check_mode: + try: + self.datacenter_obj.datastoreFolder.CreateStoragePod(name=self.datastore_cluster_name) + except Exception as generic_exc: + self.module.fail_json(msg="Failed to create datstore cluster" + " '%s' due to %s" % (self.datastore_cluster_name, + to_native(generic_exc))) + results['changed'] = True + results['result'] = "Datastore cluster '%s' created successfully." % self.datastore_cluster_name + elif state == 'absent': + results['result'] = "Datastore cluster '%s' not available or already deleted." % self.datastore_cluster_name + self.module.exit_json(**results) + + +def main(): + argument_spec = vmware_argument_spec() + argument_spec.update( + dict( + datacenter_name=dict(type='str', required=True), + datastore_cluster_name=dict(type='str', required=True), + state=dict(default='present', choices=['present', 'absent'], type='str'), + ) + ) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + datastore_cluster_mgr = VMwareDatastoreClusterManager(module) + datastore_cluster_mgr.ensure() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/vmware_datastore_cluster/aliases b/test/integration/targets/vmware_datastore_cluster/aliases new file mode 100644 index 0000000000..dcb429935f --- /dev/null +++ b/test/integration/targets/vmware_datastore_cluster/aliases @@ -0,0 +1,3 @@ +posix/ci/cloud/group4/vcenter +cloud/vcenter +destructive diff --git a/test/integration/targets/vmware_datastore_cluster/tasks/main.yml b/test/integration/targets/vmware_datastore_cluster/tasks/main.yml new file mode 100644 index 0000000000..817f349246 --- /dev/null +++ b/test/integration/targets/vmware_datastore_cluster/tasks/main.yml @@ -0,0 +1,107 @@ +# Test code for the vmware_datastore_cluster module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Make sure pyvmomi is installed + pip: + name: pyvmomi + state: latest + when: "{{ ansible_user_id == 'root' }}" + +- name: store the vcenter container ip + set_fact: + vcsim: '{{ lookup("env", "vcenter_host") }}' + +- debug: + var: vcsim + +- name: Wait for Flask controller to come up online + wait_for: + host: '{{ vcsim }}' + port: 5000 + state: started + +- name: Kill vcsim + uri: + url: http://{{ vcsim }}:5000/killall + +- name: Start vcsim + uri: + url: http://{{ vcsim }}:5000/spawn?cluster=2 + register: vcsim_instance + +- name: Wait for Flask controller to come up online + wait_for: + host: '{{ vcsim }}' + port: 443 + state: started + +- debug: + var: vcsim_instance + +- name: Get datacenter + uri: + url: http://{{ vcsim }}:5000/govc_find?filter=DC + register: datacenters + +- name: store the vcenter container ip + set_fact: + dc1: "{{ datacenters['json'][0] | basename }}" + + +- name: Add a datastore cluster to datacenter (check-mode) + vmware_datastore_cluster: &add_datastore_cluster + hostname: '{{ vcsim }}' + username: '{{ vcsim_instance.json.username }}' + password: '{{ vcsim_instance.json.password }}' + validate_certs: no + datacenter_name: "{{ dc1 }}" + datastore_cluster_name: DSC1 + state: present + check_mode: yes + register: add_dsc_check + +- assert: + that: + - add_dsc_check.changed == true + +- name: Add a datastore cluster to datacenter + vmware_datastore_cluster: *add_datastore_cluster + register: add_dsc + +- assert: + that: + - add_dsc.changed == true + +- name: Add a datastore cluster to datacenter again + vmware_datastore_cluster: *add_datastore_cluster + register: add_dsc + +- assert: + that: + - add_dsc.changed == false + +- name: Delete a datastore cluster to datacenter (check-mode) + vmware_datastore_cluster: &delete_datastore_cluster + hostname: '{{ vcsim }}' + username: '{{ vcsim_instance.json.username }}' + password: '{{ vcsim_instance.json.password }}' + validate_certs: no + datacenter_name: "{{ dc1 }}" + datastore_cluster_name: DSC1 + state: absent + check_mode: yes + register: delete_dsc_check + +- assert: + that: + - delete_dsc_check.changed == true + +# TODO: vcsim does not support delete operation on datastore cluster +#- name: Delete a datastore cluster to datacenter +# vmware_datastore_cluster: *delete_datastore_cluster +# register: delete_dsc_check + +#- assert: +# that: +# - delete_dsc_check.changed == true