From 96eccda23ba2ebdde4d8109ef585b1fe36578ae6 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Wed, 3 Jul 2019 16:01:08 +0530 Subject: [PATCH] VMware: Add module vmware_vm_storage_policy_info (#56263) Signed-off-by: Abhijeet Kasurde --- lib/ansible/module_utils/vmware.py | 7 +- lib/ansible/module_utils/vmware_spbm.py | 43 +++++ .../vmware/vmware_vm_storage_policy_info.py | 164 ++++++++++++++++++ .../vmware_vm_storage_policy_info/aliases | 3 + .../tasks/main.yml | 25 +++ test/units/module_utils/test_vmware.py | 6 +- 6 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 lib/ansible/module_utils/vmware_spbm.py create mode 100644 lib/ansible/modules/cloud/vmware/vmware_vm_storage_policy_info.py create mode 100644 test/integration/targets/vmware_vm_storage_policy_info/aliases create mode 100644 test/integration/targets/vmware_vm_storage_policy_info/tasks/main.yml diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py index acee268bcdc..f7e63e30493 100644 --- a/lib/ansible/module_utils/vmware.py +++ b/lib/ansible/module_utils/vmware.py @@ -490,7 +490,7 @@ def vmware_argument_spec(): ) -def connect_to_api(module, disconnect_atexit=True): +def connect_to_api(module, disconnect_atexit=True, return_si=False): hostname = module.params['hostname'] username = module.params['username'] password = module.params['password'] @@ -555,6 +555,8 @@ def connect_to_api(module, disconnect_atexit=True): # Also removal significantly speeds up the return of the module if disconnect_atexit: atexit.register(connect.Disconnect, service_instance) + if return_si: + return service_instance, service_instance.RetrieveContent() return service_instance.RetrieveContent() @@ -804,9 +806,8 @@ class PyVmomi(object): self.module = module self.params = module.params - self.si = None self.current_vm_obj = None - self.content = connect_to_api(self.module) + self.si, self.content = connect_to_api(self.module, return_si=True) self.custom_field_mgr = [] if self.content.customFieldsManager: # not an ESXi self.custom_field_mgr = self.content.customFieldsManager.field diff --git a/lib/ansible/module_utils/vmware_spbm.py b/lib/ansible/module_utils/vmware_spbm.py new file mode 100644 index 00000000000..168213a9f46 --- /dev/null +++ b/lib/ansible/module_utils/vmware_spbm.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2019, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +try: + from pyVmomi import vim, pbm + from pyVim.connect import SoapStubAdapter +except ImportError: + pass + +from ansible.module_utils.vmware import PyVmomi + + +class SPBM(PyVmomi): + def __init__(self, module): + super(SPBM, self).__init__(module) + self.spbm_content = None + self.spbm_si = None + self.version = "pbm.version.version2" + + def get_spbm_connection(self): + """ + Creates a Service instance for VMware SPBM + """ + client_stub = self.si._GetStub() + try: + session_cookie = client_stub.cookie.split('"')[1] + except IndexError: + self.module.fail_json(msg="Failed to get session cookie") + ssl_context = client_stub.schemeArgs.get('context') + additional_headers = {'vcSessionCookie': session_cookie} + hostname = self.module.params['hostname'] + if not hostname: + self.module.fail_json(msg="Please specify required parameter - hostname") + stub = SoapStubAdapter(host=hostname, path="/pbm/sdk", version=self.version, + sslContext=ssl_context, requestContext=additional_headers) + + self.spbm_si = pbm.ServiceInstance("ServiceInstance", stub) + self.spbm_content = self.spbm_si.PbmRetrieveServiceContent() diff --git a/lib/ansible/modules/cloud/vmware/vmware_vm_storage_policy_info.py b/lib/ansible/modules/cloud/vmware/vmware_vm_storage_policy_info.py new file mode 100644 index 00000000000..3625a4ea345 --- /dev/null +++ b/lib/ansible/modules/cloud/vmware/vmware_vm_storage_policy_info.py @@ -0,0 +1,164 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2019, Ansible Project +# Copyright: (c) 2019, 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 = r''' +--- +module: vmware_vm_storage_policy_info +short_description: Gather information about vSphere storage profile defined storage policy information. +description: +- Returns basic facts about vSphere storage profiles. +- A vSphere storage profile defines storage policy information that describes storage requirements + for virtual machines and storage capabilities of storage providers. +version_added: '2.9' +author: +- Abhijeet Kasurde (@Akasurde) +notes: +- Tested on vSphere 6.5 +requirements: +- python >= 2.6 +- PyVmomi +extends_documentation_fragment: vmware.documentation +''' + +EXAMPLES = r''' +- name: Get SPBM info + vmware_vm_storage_policy_info: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + validate_certs: no + delegate_to: localhost + register: profiles +''' + +RETURN = r''' +spbm_profiles: + description: list of dictionary of SPBM facts + returned: success + type: list + sample: [ + { + "constraints_sub_profiles": [ + { + "rule_set_info": [ + { + "id": "hostFailuresToTolerate", + "value": 1 + }, + { + "id": "stripeWidth", + "value": 1 + }, + { + "id": "forceProvisioning", + "value": false + }, + { + "id": "proportionalCapacity", + "value": 0 + }, + { + "id": "cacheReservation", + "value": 0 + } + ], + "rule_set_name": "VSAN sub-profile" + } + ], + "description": "Storage policy used as default for vSAN datastores", + "id": "aa6d5a82-1c88-45da-85d3-3d74b91a5bad", + "name": "vSAN Default Storage Policy" + }, + ] +''' + +try: + from pyVmomi import pbm +except ImportError: + pass + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.vmware_spbm import SPBM +from ansible.module_utils.vmware import vmware_argument_spec + + +class SPBMClient(SPBM): + def __init__(self, module): + super(SPBMClient, self).__init__(module) + + def show_capabilities(self, capabilities): + """ + Return property instance for given capabilities + """ + capabilities_info = [] + for capability in capabilities: + for constraint in capability.constraint: + if hasattr(constraint, 'propertyInstance'): + for propertyInstance in constraint.propertyInstance: + capabilities_info.append( + { + 'id': propertyInstance.id, + 'value': propertyInstance.value + } + ) + return capabilities_info + + def get_storage_policy_info(self): + self.get_spbm_connection() + + results = dict(changed=False, spbm_profiles=[]) + profile_manager = self.spbm_content.profileManager + profile_ids = profile_manager.PbmQueryProfile( + resourceType=pbm.profile.ResourceType(resourceType="STORAGE"), + profileCategory="REQUIREMENT" + ) + profiles = [] + if profile_ids: + profiles = profile_manager.PbmRetrieveContent(profileIds=profile_ids) + + for profile in profiles: + temp_profile_info = { + 'name': profile.name, + 'id': profile.profileId.uniqueId, + 'description': profile.description, + 'constraints_sub_profiles': [] + } + if hasattr(profile.constraints, 'subProfiles'): + subprofiles = profile.constraints.subProfiles + temp_sub_profiles = [] + for subprofile in subprofiles: + temp_sub_profiles.append({ + 'rule_set_name': subprofile.name, + 'rule_set_info': self.show_capabilities(subprofile.capability), + }) + temp_profile_info['constraints_sub_profiles'] = temp_sub_profiles + + results['spbm_profiles'].append(temp_profile_info) + + self.module.exit_json(**results) + + +def main(): + argument_spec = vmware_argument_spec() + + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + + spbm_client = SPBMClient(module) + spbm_client.get_storage_policy_info() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/vmware_vm_storage_policy_info/aliases b/test/integration/targets/vmware_vm_storage_policy_info/aliases new file mode 100644 index 00000000000..eb39c07bac0 --- /dev/null +++ b/test/integration/targets/vmware_vm_storage_policy_info/aliases @@ -0,0 +1,3 @@ +shippable/vcenter/group1 +cloud/vcenter +needs/target/prepare_vmware_tests diff --git a/test/integration/targets/vmware_vm_storage_policy_info/tasks/main.yml b/test/integration/targets/vmware_vm_storage_policy_info/tasks/main.yml new file mode 100644 index 00000000000..25bba2a8576 --- /dev/null +++ b/test/integration/targets/vmware_vm_storage_policy_info/tasks/main.yml @@ -0,0 +1,25 @@ +# Test code for the vmware_vm_storage_policy_info module. +# Copyright: (c) 2019, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- when: vcsim is not defined + block: + - &spbm_data + name: Get information about vSphere SPBM info + vmware_vm_storage_policy_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: no + register: spbm_data + + - &spbm_check + assert: + that: + - spbm_data.spbm_profiles is defined + + - <<: *spbm_data + name: Get information about vSphere SPBM info in check mode + check_mode: yes + + - <<: *spbm_check \ No newline at end of file diff --git a/test/units/module_utils/test_vmware.py b/test/units/module_utils/test_vmware.py index 9836f133968..e19e305daad 100644 --- a/test/units/module_utils/test_vmware.py +++ b/test/units/module_utils/test_vmware.py @@ -88,10 +88,12 @@ class FakeAnsibleModule: raise FailJson(*args, **kwargs) -def fake_connect_to_api(module): +def fake_connect_to_api(module, disconnect_atexit=True, return_si=False): class MyContent(): customFieldsManager = None - return MyContent + if return_si: + return (None, MyContent()) + return MyContent() def test_pyvmomi_lib_exists(mocker, fake_ansible_module):