# -*- 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 import ssl # requests is required for exception handling of the ConnectionError import requests from pyVim import connect from pyVmomi import vim HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False class TaskError(Exception): pass def wait_for_task(task): while True: 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: if pg.name == portgroup_name: return pg return None def find_entity_child_by_path(content, entityRootFolder, path): entity = entityRootFolder searchIndex = content.searchIndex paths = path.split("/") try: for path in paths: entity = searchIndex.FindChild (entity, path) if entity.name == paths[-1]: return entity except: pass return None # Maintain for legacy, or remove with 2.1 ? # Should be replaced with find_cluster_by_name def find_cluster_by_name_datacenter(datacenter, cluster_name): host_folder = datacenter.hostFolder for folder in host_folder.childEntity: if folder.name == cluster_name: return folder return None def find_cluster_by_name(content, cluster_name, datacenter=None): if datacenter: folder = datacenter.hostFolder else: folder = content.rootFolder clusters = get_all_objs(content, [vim.ClusterComputeResource], folder) for cluster in clusters: if cluster.name == cluster_name: return cluster return None def find_datacenter_by_name(content, datacenter_name): 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): 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): host_system = get_all_objs(content, [vim.HostSystem]) for host in host_system: if host.name == hostname: return host return None def find_vm_by_id(content, vm_id, vm_id_type="vm_name", datacenter=None, cluster=None): """ UUID is unique to a VM, every other id returns the first match. """ si = content.searchIndex vm = None if vm_id_type == 'dns_name': vm = si.FindByDnsName(datacenter=datacenter, dnsName=vm_id, vmSearch=True) elif vm_id_type == 'inventory_path': vm = si.FindByInventoryPath(inventoryPath=vm_id) if type(vm) != type(vim.VirtualMachine): vm = None elif vm_id_type == 'uuid': vm = si.FindByUuid(datacenter=datacenter, instanceUuid=vm_id, vmSearch=True) elif vm_id_type == 'ip': vm = si.FindByIp(datacenter=datacenter, ip=vm_id, vmSearch=True) elif vm_id_type == 'vm_name': folder = None if cluster: folder = cluster elif datacenter: folder = datacenter.hostFolder vm = find_vm_by_name(content, vm_id, folder) return vm def find_vm_by_name(content, vm_name, folder=None, recurse=True): vms = get_all_objs(content, [vim.VirtualMachine], folder, recurse=True) for vm in vms: if vm.name == vm_name: return vm return None def find_host_portgroup_by_name(host, portgroup_name): for portgroup in host.config.network.portgroup: if portgroup.spec.name == portgroup_name: return portgroup return None 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), validate_certs=dict(type='bool', required=False, default=True), ) def connect_to_api(module, disconnect_atexit=True): hostname = module.params['hostname'] username = module.params['username'] password = module.params['password'] validate_certs = module.params['validate_certs'] if validate_certs and not hasattr(ssl, 'SSLContext'): module.fail_json(msg='pyVim does not support changing verification mode with python < 2.7.9. Either update python or or use validate_certs=false') try: service_instance = connect.SmartConnect(host=hostname, user=username, pwd=password) except vim.fault.InvalidLogin as invalid_login: module.fail_json(msg=invalid_login.msg, apierror=str(invalid_login)) except (requests.ConnectionError, ssl.SSLError) as connection_error: if '[SSL: CERTIFICATE_VERIFY_FAILED]' in str(connection_error) and not validate_certs: context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) context.verify_mode = ssl.CERT_NONE service_instance = connect.SmartConnect(host=hostname, user=username, pwd=password, sslContext=context) else: module.fail_json(msg="Unable to connect to vCenter or ESXi API on TCP/443.", apierror=str(connection_error)) # 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() def get_all_objs(content, vimtype, folder=None, recurse=True): if not folder: folder = content.rootFolder obj = {} container = content.viewManager.CreateContainerView(folder, vimtype, recurse) for managed_object_ref in container.view: obj.update({managed_object_ref: managed_object_ref.name}) return obj