diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py index 08837a577dd..411a71bdafc 100644 --- a/lib/ansible/module_utils/vmware.py +++ b/lib/ansible/module_utils/vmware.py @@ -1147,3 +1147,14 @@ class PyVmomi(object): self.module.fail_json(changed=False, msg="ESXi '%s' not found" % esxi_host_name) return host_obj_list + + def find_datastore_by_name(self, datastore_name): + """ + Function to get datastore managed object by name + Args: + datastore_name: Name of datastore + + Returns: datastore managed object if found else None + + """ + return find_datastore_by_name(self.content, datastore_name=datastore_name) diff --git a/lib/ansible/modules/cloud/vmware/vmware_datastore_maintenancemode.py b/lib/ansible/modules/cloud/vmware/vmware_datastore_maintenancemode.py new file mode 100644 index 00000000000..159b8c59497 --- /dev/null +++ b/lib/ansible/modules/cloud/vmware/vmware_datastore_maintenancemode.py @@ -0,0 +1,205 @@ +#!/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_maintenancemode +short_description: Place a datastore into maintenance mode +description: + - This module can be used to manage maintenance mode of a datastore. +author: +- "Abhijeet Kasurde (@akasurde)" +version_added: 2.6 +notes: + - Tested on vSphere 5.5, 6.0 and 6.5 +requirements: + - "python >= 2.6" + - PyVmomi +options: + datastore: + description: + - Name of datastore to manage. + - If C(datastore_cluster) or C(cluster_name) are not set, this parameter is required. + datastore_cluster: + description: + - Name of the datastore cluster from all child datastores to be managed. + - If C(datastore) or C(cluster_name) are not set, this parameter is required. + cluster_name: + description: + - Name of the cluster where datastore is connected to. + - If multiple datastores are connected to the given cluster, then all datastores will be managed by C(state). + - If C(datastore) or C(datastore_cluster) are not set, this parameter is required. + state: + description: + - If set to C(present), then enter datastore into maintenance mode. + - If set to C(present) and datastore is already in maintenance mode, then no action will be taken. + - If set to C(absent) and datastore is in maintenance mode, then exit maintenance mode. + - If set to C(absent) and datastore is not in maintenance mode, then no action will be taken. + choices: [ present, absent ] + default: present + required: False +extends_documentation_fragment: vmware.documentation +''' + +EXAMPLES = ''' +- name: Enter datastore into Maintenance Mode + vmware_datastore_maintenancemode: + hostname: vc_host + username: vc_user + password: vc_pass + datastore: datastore1 + state: present + +- name: Enter all datastores under cluster into Maintenance Mode + vmware_datastore_maintenancemode: + hostname: vc_host + username: vc_user + password: vc_pass + cluster_name: DC0_C0 + state: present + +- name: Enter all datastores under datastore cluster into Maintenance Mode + vmware_datastore_maintenancemode: + hostname: vc_host + username: vc_user + password: vc_pass + datastore_cluster: DSC_POD0 + state: present + +- name: Exit datastore into Maintenance Mode + vmware_datastore_maintenancemode: + hostname: vc_host + username: vc_user + password: vc_pass + datastore: datastore1 + state: absent +''' + +RETURN = ''' +results: + description: Action taken for datastore + returned: always + type: dict + sample: +''' + +try: + from pyVmomi import vim +except ImportError: + pass + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.vmware import (PyVmomi, vmware_argument_spec, wait_for_task, + find_cluster_by_name, get_all_objs) +from ansible.module_utils._text import to_native + + +class VmwareDatastoreMaintenanceMgr(PyVmomi): + def __init__(self, module): + super(VmwareDatastoreMaintenanceMgr, self).__init__(module) + datastore_name = self.params.get('datastore') + cluster_name = self.params.get('cluster_name') + datastore_cluster = self.params.get('datastore_cluster') + self.datastore_objs = [] + if datastore_name: + self.datastore_objs = [self.find_datastore_by_name(datastore_name=datastore_name)] + elif cluster_name: + cluster = find_cluster_by_name(self.content, cluster_name) + if not cluster: + self.module.fail_json(msg='Failed to find cluster "%(cluster_name)s".' % self.params) + self.datastore_objs = cluster.datastore + elif datastore_cluster: + datastore_cluster_obj = get_all_objs(self.content, [vim.StoragePod]) + if not datastore_cluster_obj: + self.module.fail_json(msg='Failed to find datastore cluster "%(datastore_cluster)s".' % self.params) + for datastore in datastore_cluster_obj.childEntity: + self.datastore_objs.append(datastore) + else: + self.module.fail_json(msg="Please select one of 'cluster_name', 'datastore' or 'datastore_cluster'.") + self.state = self.params.get('state') + + def ensure(self): + datastore_results = dict() + change_datastore_list = [] + for datastore in self.datastore_objs: + changed = False + if self.state == 'present' and datastore.summary.maintenanceMode != 'normal': + datastore_results[datastore.name] = "Datastore '%s' is already in maintenance mode." % datastore.name + break + elif self.state == 'absent' and datastore.summary.maintenanceMode == 'normal': + datastore_results[datastore.name] = "Datastore '%s' is not in maintenance mode." % datastore.name + break + + try: + if self.state == 'present': + storage_replacement_result = datastore.DatastoreEnterMaintenanceMode() + task = storage_replacement_result.task + else: + task = datastore.DatastoreExitMaintenanceMode_Task() + + success, result = wait_for_task(task) + + if success: + changed = True + if self.state == 'present': + datastore_results[datastore.name] = "Datastore '%s' entered in maintenance mode." % datastore.name + else: + datastore_results[datastore.name] = "Datastore '%s' exited from maintenance mode." % datastore.name + except vim.fault.InvalidState as invalid_state: + if self.state == 'present': + msg = "Unable to enter datastore '%s' in" % datastore.name + else: + msg = "Unable to exit datastore '%s' from" % datastore.name + msg += " maintenance mode due to : %s" % to_native(invalid_state.msg) + self.module.fail_json(msg=msg) + except Exception as exc: + if self.state == 'present': + msg = "Unable to enter datastore '%s' in" % datastore.name + else: + msg = "Unable to exit datastore '%s' from" % datastore.name + msg += " maintenance mode due to generic exception : %s" % to_native(exc) + self.module.fail_json(msg=msg) + change_datastore_list.append(changed) + + changed = False + if any(change_datastore_list): + changed = True + self.module.exit_json(changed=changed, results=datastore_results) + + +def main(): + spec = vmware_argument_spec() + spec.update(dict( + datastore=dict(type='str', required=False), + cluster_name=dict(type='str', required=False), + datastore_cluster=dict(type='str', required=False), + state=dict(type='str', default='present', choices=['present', 'absent']), + )) + + module = AnsibleModule( + argument_spec=spec, + required_one_of=[ + ['datastore', 'cluster_name', 'datastore_cluster'], + ], + ) + + datastore_maintenance_mgr = VmwareDatastoreMaintenanceMgr(module=module) + datastore_maintenance_mgr.ensure() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/vmware_datastore_maintenancemode/aliases b/test/integration/targets/vmware_datastore_maintenancemode/aliases new file mode 100644 index 00000000000..2dc01c7e889 --- /dev/null +++ b/test/integration/targets/vmware_datastore_maintenancemode/aliases @@ -0,0 +1,2 @@ +cloud/vcenter +destructive diff --git a/test/integration/targets/vmware_datastore_maintenancemode/tasks/main.yml b/test/integration/targets/vmware_datastore_maintenancemode/tasks/main.yml new file mode 100644 index 00000000000..f709f9cc8f1 --- /dev/null +++ b/test/integration/targets/vmware_datastore_maintenancemode/tasks/main.yml @@ -0,0 +1,114 @@ +# Test code for the vmware_datastore_maintenancemode module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# TODO: vcsim does not support datastore maintenance mode properties +#- 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?datacenter=1&cluster=1&folder=0 +# register: vcsim_instance + +#- name: Wait for vcsim server to come up online +# wait_for: +# host: "{{ vcsim }}" +# port: 443 +# state: started + +#- name: Get a list of datastores from vcsim +# uri: +# url: http://{{ vcsim }}:5000/govc_find?filter=D +# register: datastores + +#- name: Enter datastore in maintenance mode +# vmware_datastore_maintenancemode: +# hostname: "{{ vcsim }}" +# username: "{{ vcsim_instance.json.username }}" +# password: "{{ vcsim_instance.json.password }}" +# state: present +# datastore: "{{ item | basename }}" +# validate_certs: False +# with_items: "{{ datastores['json'] }}" +# register: test_result_0001 + +#- debug: var=test_result_0001 + +#- name: assert that changes were made +# assert: +# that: +# - "test_result_0001.results|map(attribute='changed')|unique|list == [true]" + +#- name: Enter datastore in maintenance mode again +# vmware_datastore_maintenancemode: +# hostname: "{{ vcsim }}" +# username: "{{ vcsim_instance.json.username }}" +# password: "{{ vcsim_instance.json.password }}" +# state: present +# datastore: "{{ item | basename }}" +# validate_certs: no +# with_items: "{{ datastores['json'] }}" +# register: test_result_0002 + +#- debug: var=test_result_0002 + +#- name: assert that no changes were made +# assert: +# that: +# - "test_result_0002.results|map(attribute='changed')|unique|list == [false]" + +#- name: Exit datastores from maintenance mode +# vmware_datastore_maintenancemode: +# hostname: "{{ vcsim }}" +# username: "{{ vcsim_instance.json.username }}" +# password: "{{ vcsim_instance.json.password }}" +# state: absent +# esxi_hostname: "{{ item | basename }}" +# validate_certs: no +# with_items: "{{ datastores['json'] }}" +# register: test_result_0003 + +#- debug: var=test_result_0003 + +#- name: assert that changes were made +# assert: +# that: +# - "test_result_0003.results|map(attribute='changed')|unique|list == [true]" + +#- name: Exit datastores from maintenance mode again +# vmware_datastore_maintenancemode: +# hostname: "{{ vcsim }}" +# username: "{{ vcsim_instance.json.username }}" +# password: "{{ vcsim_instance.json.password }}" +# state: absent +# esxi_hostname: "{{ item | basename }}" +# validate_certs: no +# with_items: "{{ datastores['json'] }}" +# register: test_result_0004 + +#- debug: var=test_result_0004 + +#- name: assert that no changes were made +# assert: +# that: +# - "test_result_0004.results|map(attribute='changed')|unique|list == [false]"