Skip to content

Commit 8e2503b

Browse files
committed
De-couple backend logic from module logic
1 parent 09c21d5 commit 8e2503b

File tree

2 files changed

+102
-5
lines changed

2 files changed

+102
-5
lines changed
+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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 ApiAccessError(Exception):
23+
def __init__(self, *args, **kwargs):
24+
super(ApiAccessError, self).__init__(*args, **kwargs)
25+
26+
class PyvmomiClient(object):
27+
def __init__(self, hostname, username, password, port=443, validate_certs=True, http_proxy_host=None, http_proxy_port=None):
28+
self.si, self.content = self._connect_to_api(hostname, username, password, port, validate_certs, http_proxy_host, http_proxy_port)
29+
30+
def _connect_to_api(self, hostname, username, password, port, validate_certs, http_proxy_host, http_proxy_port):
31+
if not hostname:
32+
raise ApiAccessError("Hostname parameter is missing.")
33+
34+
if not username:
35+
raise ApiAccessError("Username parameter is missing.")
36+
37+
if not password:
38+
raise ApiAccessError("Password parameter is missing.")
39+
40+
if validate_certs and not hasattr(ssl, 'SSLContext'):
41+
_raise_or_fail(msg='pyVim does not support changing verification mode with python < 2.7.9. Either update '
42+
'python or use validate_certs=false.')
43+
elif validate_certs:
44+
ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
45+
ssl_context.verify_mode = ssl.CERT_REQUIRED
46+
ssl_context.check_hostname = True
47+
ssl_context.load_default_certs()
48+
elif hasattr(ssl, 'SSLContext'):
49+
ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
50+
ssl_context.verify_mode = ssl.CERT_NONE
51+
ssl_context.check_hostname = False
52+
53+
service_instance = None
54+
55+
connect_args = dict(
56+
host=hostname,
57+
port=port,
58+
)
59+
if ssl_context:
60+
connect_args.update(sslContext=ssl_context)
61+
62+
msg_suffix = ''
63+
try:
64+
if http_proxy_host:
65+
msg_suffix = " [proxy: %s:%d]" % (http_proxy_host, http_proxy_port)
66+
connect_args.update(httpProxyHost=http_proxy_host, httpProxyPort=http_proxy_port)
67+
smart_stub = connect.SmartStubAdapter(**connect_args)
68+
session_stub = connect.VimSessionOrientedStub(smart_stub, connect.VimSessionOrientedStub.makeUserLoginMethod(username, password))
69+
service_instance = vim.ServiceInstance('ServiceInstance', session_stub)
70+
else:
71+
connect_args.update(user=username, pwd=password)
72+
service_instance = connect.SmartConnect(**connect_args)
73+
except vim.fault.InvalidLogin as invalid_login:
74+
msg = "Unable to log on to vCenter or ESXi API at %s:%s " % (hostname, port)
75+
_raise_or_fail(msg="%s as %s: %s" % (msg, username, invalid_login.msg) + msg_suffix)
76+
except vim.fault.NoPermission as no_permission:
77+
_raise_or_fail(msg="User %s does not have required permission"
78+
" to log on to vCenter or ESXi API at %s:%s : %s" % (username, hostname, port, no_permission.msg))
79+
except (requests.ConnectionError, ssl.SSLError) as generic_req_exc:
80+
_raise_or_fail(msg="Unable to connect to vCenter or ESXi API at %s on TCP/%s: %s" % (hostname, port, generic_req_exc))
81+
except vmodl.fault.InvalidRequest as invalid_request:
82+
# Request is malformed
83+
msg = "Failed to get a response from server %s:%s " % (hostname, port)
84+
_raise_or_fail(msg="%s as request is malformed: %s" % (msg, invalid_request.msg) + msg_suffix)
85+
except Exception as generic_exc:
86+
msg = "Unknown error while connecting to vCenter or ESXi API at %s:%s" % (hostname, port) + msg_suffix
87+
_raise_or_fail(msg="%s : %s" % (msg, generic_exc))
88+
89+
if service_instance is None:
90+
msg = "Unknown error while connecting to vCenter or ESXi API at %s:%s" % (hostname, port)
91+
_raise_or_fail(msg=msg + msg_suffix)
92+
93+
atexit.register(connect.Disconnect, service_instance)
94+
95+
return service_instance, service_instance.RetrieveContent()

plugins/module_utils/vmware.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
77
# SPDX-License-Identifier: GPL-3.0-or-later
88

9-
from __future__ import absolute_import, division, print_function
109
__metaclass__ = type
1110

1211
import atexit
@@ -22,6 +21,7 @@
2221
import datetime
2322
from collections import OrderedDict
2423
from ansible.module_utils.compat.version import StrictVersion
24+
from ansible_collections.community.vmware.plugins.module_utils.clients._vmware import PyvmomiClient
2525
from random import randint
2626

2727

@@ -1067,11 +1067,8 @@ def quote_obj_name(object_name=None):
10671067
return object_name
10681068

10691069

1070-
class PyVmomi(object):
1070+
class PyVmomi(PyvmomiClient):
10711071
def __init__(self, module):
1072-
"""
1073-
Constructor
1074-
"""
10751072
if not HAS_REQUESTS:
10761073
module.fail_json(msg=missing_required_lib('requests'),
10771074
exception=REQUESTS_IMP_ERR)
@@ -1080,6 +1077,11 @@ def __init__(self, module):
10801077
module.fail_json(msg=missing_required_lib('PyVmomi'),
10811078
exception=PYVMOMI_IMP_ERR)
10821079

1080+
super().__init__(module.params['hostname'], module.params['username'],
1081+
module.params['password'], module.params['port'],
1082+
module.params['validate_certs'],
1083+
module.params['proxy_host'],
1084+
module.params['proxy_port'])
10831085
self.module = module
10841086
self.params = module.params
10851087
self.current_vm_obj = None

0 commit comments

Comments
 (0)