|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | + |
| 3 | +# Copyright: (c) 2015, Joseph Callen <jcallen () csc.com> |
| 4 | +# Copyright: (c) 2018, Ansible Project |
| 5 | +# Copyright: (c) 2018, James E. King III (@jeking3) <jking@apache.org> |
| 6 | +# Copyright: (c) 2025, Mario Lenz (@mariolenz) <m@riolenz.de> |
| 7 | +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) |
| 8 | +# SPDX-License-Identifier: GPL-3.0-or-later |
| 9 | + |
| 10 | +__metaclass__ = type |
| 11 | + |
| 12 | +import atexit |
| 13 | +import requests |
| 14 | +import ssl |
| 15 | +import traceback |
| 16 | + |
| 17 | +from ansible.module_utils.basic import missing_required_lib |
| 18 | +from pyVim import connect |
| 19 | +from pyVmomi import vim, vmodl |
| 20 | + |
| 21 | + |
| 22 | +class PyvmomiClient(object): |
| 23 | + def __init__(self, hostname, username, password, port=443, validate_certs=True, http_proxy_host=None, http_proxy_port=None): |
| 24 | + self.si, self.content = self._connect_to_api(hostname, username, password, port, validate_certs, http_proxy_host, http_proxy_port) |
| 25 | + |
| 26 | + def _connect_to_api(self, hostname, username, password, port, validate_certs, http_proxy_host, http_proxy_port): |
| 27 | + def _raise_or_fail(msg): |
| 28 | + if module is not None: |
| 29 | + module.fail_json(msg=msg) |
| 30 | + raise ApiAccessError(msg) |
| 31 | + |
| 32 | + if not hostname: |
| 33 | + _raise_or_fail(msg="Hostname parameter is missing." |
| 34 | + " Please specify this parameter in task or" |
| 35 | + " export environment variable like 'export VMWARE_HOST=ESXI_HOSTNAME'") |
| 36 | + |
| 37 | + if not username: |
| 38 | + _raise_or_fail(msg="Username parameter is missing." |
| 39 | + " Please specify this parameter in task or" |
| 40 | + " export environment variable like 'export VMWARE_USER=ESXI_USERNAME'") |
| 41 | + |
| 42 | + if not password: |
| 43 | + _raise_or_fail(msg="Password parameter is missing." |
| 44 | + " Please specify this parameter in task or" |
| 45 | + " export environment variable like 'export VMWARE_PASSWORD=ESXI_PASSWORD'") |
| 46 | + |
| 47 | + if validate_certs and not hasattr(ssl, 'SSLContext'): |
| 48 | + _raise_or_fail(msg='pyVim does not support changing verification mode with python < 2.7.9. Either update ' |
| 49 | + 'python or use validate_certs=false.') |
| 50 | + elif validate_certs: |
| 51 | + ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) |
| 52 | + ssl_context.verify_mode = ssl.CERT_REQUIRED |
| 53 | + ssl_context.check_hostname = True |
| 54 | + ssl_context.load_default_certs() |
| 55 | + elif hasattr(ssl, 'SSLContext'): |
| 56 | + ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) |
| 57 | + ssl_context.verify_mode = ssl.CERT_NONE |
| 58 | + ssl_context.check_hostname = False |
| 59 | + |
| 60 | + service_instance = None |
| 61 | + |
| 62 | + connect_args = dict( |
| 63 | + host=hostname, |
| 64 | + port=port, |
| 65 | + ) |
| 66 | + if ssl_context: |
| 67 | + connect_args.update(sslContext=ssl_context) |
| 68 | + |
| 69 | + msg_suffix = '' |
| 70 | + try: |
| 71 | + if http_proxy_host: |
| 72 | + msg_suffix = " [proxy: %s:%d]" % (http_proxy_host, http_proxy_port) |
| 73 | + connect_args.update(httpProxyHost=http_proxy_host, httpProxyPort=http_proxy_port) |
| 74 | + smart_stub = connect.SmartStubAdapter(**connect_args) |
| 75 | + session_stub = connect.VimSessionOrientedStub(smart_stub, connect.VimSessionOrientedStub.makeUserLoginMethod(username, password)) |
| 76 | + service_instance = vim.ServiceInstance('ServiceInstance', session_stub) |
| 77 | + else: |
| 78 | + connect_args.update(user=username, pwd=password) |
| 79 | + service_instance = connect.SmartConnect(**connect_args) |
| 80 | + except vim.fault.InvalidLogin as invalid_login: |
| 81 | + msg = "Unable to log on to vCenter or ESXi API at %s:%s " % (hostname, port) |
| 82 | + _raise_or_fail(msg="%s as %s: %s" % (msg, username, invalid_login.msg) + msg_suffix) |
| 83 | + except vim.fault.NoPermission as no_permission: |
| 84 | + _raise_or_fail(msg="User %s does not have required permission" |
| 85 | + " to log on to vCenter or ESXi API at %s:%s : %s" % (username, hostname, port, no_permission.msg)) |
| 86 | + except (requests.ConnectionError, ssl.SSLError) as generic_req_exc: |
| 87 | + _raise_or_fail(msg="Unable to connect to vCenter or ESXi API at %s on TCP/%s: %s" % (hostname, port, generic_req_exc)) |
| 88 | + except vmodl.fault.InvalidRequest as invalid_request: |
| 89 | + # Request is malformed |
| 90 | + msg = "Failed to get a response from server %s:%s " % (hostname, port) |
| 91 | + _raise_or_fail(msg="%s as request is malformed: %s" % (msg, invalid_request.msg) + msg_suffix) |
| 92 | + except Exception as generic_exc: |
| 93 | + msg = "Unknown error while connecting to vCenter or ESXi API at %s:%s" % (hostname, port) + msg_suffix |
| 94 | + _raise_or_fail(msg="%s : %s" % (msg, generic_exc)) |
| 95 | + |
| 96 | + if service_instance is None: |
| 97 | + msg = "Unknown error while connecting to vCenter or ESXi API at %s:%s" % (hostname, port) |
| 98 | + _raise_or_fail(msg=msg + msg_suffix) |
| 99 | + |
| 100 | + atexit.register(connect.Disconnect, service_instance) |
| 101 | + |
| 102 | + return service_instance, service_instance.RetrieveContent() |
0 commit comments