From 986910be5d96d90da2105c736547d197ce33789e Mon Sep 17 00:00:00 2001 From: Joseph Callen Date: Fri, 17 Apr 2015 14:48:57 -0400 Subject: [PATCH 1/2] Adding a new VMware utilities module --- lib/ansible/module_utils/vmware.py | 181 +++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 lib/ansible/module_utils/vmware.py diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py new file mode 100644 index 0000000000..d7dcc256fe --- /dev/null +++ b/lib/ansible/module_utils/vmware.py @@ -0,0 +1,181 @@ +#!/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 . + + +try: + import atexit + import time + # requests is required for exception handling of the ConnectionError + import requests + from pyVim import connect + from pyVmomi import vim, vmodl + HAS_PYVMOMI = True +except ImportError: + HAS_PYVMOMI = False + + +class TaskError(Exception): + pass + + +def task_success(task): + return True + + +def task_running(task): + time.sleep(15) + return False + + +def task_error(task): + + try: + raise TaskError(task.info.error) + except AttributeError: + raise TaskError("Unknown error has occurred") + + +def task_queued(task): + time.sleep(15) + return False + + +def wait_for_task(task): + + task_state = { + vim.TaskInfo.State.success: task_success, + vim.TaskInfo.State.running: task_running, + vim.TaskInfo.State.queued: task_queued, + vim.TaskInfo.State.error: task_error, + } + + while True: + try: + is_finished = task_state[task.info.state](task) + if is_finished: + return True, task.info.result + # This exception should be handled in the module that calls this method + # and fail with an appropriate message to module.fail_json() + except TaskError: + raise + + +def find_dvspg_by_name(dv_switch, portgroup_name): + portgroups = dv_switch.portgroup + + for pg in portgroups: + if pg.name == portgroup_name: + return pg + + return None + + +def find_cluster_by_name_datacenter(datacenter, cluster_name): + try: + host_folder = datacenter.hostFolder + for folder in host_folder.childEntity: + if folder.name == cluster_name: + return folder + return None + # This exception should be handled in the module that calls this method + # and fail with an appropriate message to module.fail_json() + except vmodl.MethodFault: + raise + + +def find_datacenter_by_name(content, datacenter_name, throw=True): + try: + datacenters = get_all_objs(content, [vim.Datacenter]) + for dc in datacenters: + if dc.name == datacenter_name: + return dc + + return None + # This exception should be handled in the module that calls this method + # and fail with an appropriate message to module.fail_json() + except vmodl.MethodFault: + raise + + +def find_dvs_by_name(content, switch_name): + try: + vmware_distributed_switches = get_all_objs(content, [vim.dvs.VmwareDistributedVirtualSwitch]) + for dvs in vmware_distributed_switches: + if dvs.name == switch_name: + return dvs + return None + # This exception should be handled in the module that calls this method + # and fail with an appropriate message to module.fail_json() + except vmodl.MethodFault: + raise + + +def find_hostsystem_by_name(content, hostname): + try: + host_system = get_all_objs(content, [vim.HostSystem]) + for host in host_system: + if host.name == hostname: + return host + return None + # This exception should be handled in the module that calls this method + # and fail with an appropriate message to module.fail_json() + except vmodl.MethodFault: + raise + + +def vmware_argument_spec(): + return dict( + hostname=dict(type='str', required=True), + username=dict(type='str', aliases=['user', 'admin'], required=True), + password=dict(type='str', aliases=['pass', 'pwd'], required=True, no_log=True), + ) + + +def connect_to_api(module, disconnect_atexit=True): + hostname = module.params['hostname'] + username = module.params['username'] + password = module.params['password'] + try: + service_instance = connect.SmartConnect(host=hostname, user=username, pwd=password) + + # Disabling atexit should be used in special cases only. + # Such as IP change of the ESXi host which removes the connection anyway. + # Also removal significantly speeds up the return of the module + + if disconnect_atexit: + atexit.register(connect.Disconnect, service_instance) + return service_instance.RetrieveContent() + except vim.fault.InvalidLogin as invalid_login: + module.fail_json(msg=invalid_login.msg) + except requests.ConnectionError: + module.fail_json(msg="Unable to connect to vCenter or ESXi API on TCP/443.") + + +def get_all_objs(content, vimtype): + try: + obj = {} + container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True) + for managed_object_ref in container.view: + obj.update({managed_object_ref: managed_object_ref.name}) + return obj + # This exception should be handled in the module that calls this method + # and fail with an appropriate message to module.fail_json() + except vmodl.MethodFault: + raise \ No newline at end of file From 9b317858c1c5a2f74cd55df4a5dfecf427a01594 Mon Sep 17 00:00:00 2001 From: Joseph Callen Date: Tue, 21 Apr 2015 08:33:32 -0400 Subject: [PATCH 2/2] Modified per @bcoca Removed try/except raises Modified wait_for_task Added api exception error message --- lib/ansible/module_utils/vmware.py | 138 ++++++++++------------------- 1 file changed, 47 insertions(+), 91 deletions(-) diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py index d7dcc256fe..5d94b9d6bb 100644 --- a/lib/ansible/module_utils/vmware.py +++ b/lib/ansible/module_utils/vmware.py @@ -1,4 +1,3 @@ -#!/bin/python # -*- coding: utf-8 -*- # (c) 2015, Joseph Callen @@ -35,49 +34,24 @@ class TaskError(Exception): pass -def task_success(task): - return True - - -def task_running(task): - time.sleep(15) - return False - - -def task_error(task): - - try: - raise TaskError(task.info.error) - except AttributeError: - raise TaskError("Unknown error has occurred") - - -def task_queued(task): - time.sleep(15) - return False - - def wait_for_task(task): - task_state = { - vim.TaskInfo.State.success: task_success, - vim.TaskInfo.State.running: task_running, - vim.TaskInfo.State.queued: task_queued, - vim.TaskInfo.State.error: task_error, - } - while True: - try: - is_finished = task_state[task.info.state](task) - if is_finished: - return True, task.info.result - # This exception should be handled in the module that calls this method - # and fail with an appropriate message to module.fail_json() - except TaskError: - raise + if task.info.state == vim.TaskInfo.State.success: + return True, task.info.result + if task.info.state == vim.TaskInfo.State.error + try: + raise TaskError(task.info.error) + except AttributeError: + raise TaskError("An unknown error has occurred") + if task.info.state == vim.TaskInfo.State.running: + time.sleep(15) + if task.info.state = vim.TaskInfo.State.queued: + time.sleep(15) def find_dvspg_by_name(dv_switch, portgroup_name): + portgroups = dv_switch.portgroup for pg in portgroups: @@ -88,59 +62,44 @@ def find_dvspg_by_name(dv_switch, portgroup_name): def find_cluster_by_name_datacenter(datacenter, cluster_name): - try: - host_folder = datacenter.hostFolder - for folder in host_folder.childEntity: - if folder.name == cluster_name: - return folder - return None - # This exception should be handled in the module that calls this method - # and fail with an appropriate message to module.fail_json() - except vmodl.MethodFault: - raise + + host_folder = datacenter.hostFolder + for folder in host_folder.childEntity: + if folder.name == cluster_name: + return folder + return None def find_datacenter_by_name(content, datacenter_name, throw=True): - try: - datacenters = get_all_objs(content, [vim.Datacenter]) - for dc in datacenters: - if dc.name == datacenter_name: - return dc - return None - # This exception should be handled in the module that calls this method - # and fail with an appropriate message to module.fail_json() - except vmodl.MethodFault: - raise + datacenters = get_all_objs(content, [vim.Datacenter]) + for dc in datacenters: + if dc.name == datacenter_name: + return dc + + return None def find_dvs_by_name(content, switch_name): - try: - vmware_distributed_switches = get_all_objs(content, [vim.dvs.VmwareDistributedVirtualSwitch]) - for dvs in vmware_distributed_switches: - if dvs.name == switch_name: - return dvs - return None - # This exception should be handled in the module that calls this method - # and fail with an appropriate message to module.fail_json() - except vmodl.MethodFault: - raise + + vmware_distributed_switches = get_all_objs(content, [vim.dvs.VmwareDistributedVirtualSwitch]) + for dvs in vmware_distributed_switches: + if dvs.name == switch_name: + return dvs + return None def find_hostsystem_by_name(content, hostname): - try: - host_system = get_all_objs(content, [vim.HostSystem]) - for host in host_system: - if host.name == hostname: - return host - return None - # This exception should be handled in the module that calls this method - # and fail with an appropriate message to module.fail_json() - except vmodl.MethodFault: - raise + + host_system = get_all_objs(content, [vim.HostSystem]) + for host in host_system: + if host.name == hostname: + return host + return None def vmware_argument_spec(): + return dict( hostname=dict(type='str', required=True), username=dict(type='str', aliases=['user', 'admin'], required=True), @@ -149,6 +108,7 @@ def vmware_argument_spec(): def connect_to_api(module, disconnect_atexit=True): + hostname = module.params['hostname'] username = module.params['username'] password = module.params['password'] @@ -163,19 +123,15 @@ def connect_to_api(module, disconnect_atexit=True): atexit.register(connect.Disconnect, service_instance) return service_instance.RetrieveContent() except vim.fault.InvalidLogin as invalid_login: - module.fail_json(msg=invalid_login.msg) - except requests.ConnectionError: - module.fail_json(msg="Unable to connect to vCenter or ESXi API on TCP/443.") + module.fail_json(msg=invalid_login.msg, apierror=str(invalid_login)) + except requests.ConnectionError as connection_error: + module.fail_json(msg="Unable to connect to vCenter or ESXi API on TCP/443.", apierror=str(connection_error)) def get_all_objs(content, vimtype): - try: - obj = {} - container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True) - for managed_object_ref in container.view: - obj.update({managed_object_ref: managed_object_ref.name}) - return obj - # This exception should be handled in the module that calls this method - # and fail with an appropriate message to module.fail_json() - except vmodl.MethodFault: - raise \ No newline at end of file + + obj = {} + container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True) + for managed_object_ref in container.view: + obj.update({managed_object_ref: managed_object_ref.name}) + return obj