Skip to content

Commit 385dd4c

Browse files
committed
Move non-controller code in 'module_utils/cm_utils' into new 'cm_controller_utils.py' file
Signed-off-by: Webster Mudge <[email protected]>
1 parent f893ff2 commit 385dd4c

File tree

4 files changed

+165
-132
lines changed

4 files changed

+165
-132
lines changed

plugins/lookup/cm_service.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@
9494
- Requires C(cm_client).
9595
'''
9696

97-
from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import ClouderaManagerLookupBase
97+
from ansible_collections.cloudera.cluster.plugins.module_utils.cm_controller_utils import ClouderaManagerLookupBase
98+
9899

99100
class LookupModule(ClouderaManagerLookupBase):
100101
def run(self, terms, variables=None, **kwargs):
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright 2023 Cloudera, Inc.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
"""
19+
A common Ansible plugin functions for Cloudera Manager
20+
"""
21+
22+
import json
23+
import logging
24+
25+
from urllib3 import disable_warnings
26+
from urllib3.exceptions import InsecureRequestWarning, MaxRetryError, HTTPError
27+
from urllib3.util import Url
28+
from urllib.parse import urljoin
29+
30+
from ansible.errors import AnsibleError
31+
from ansible.module_utils.common.text.converters import to_text
32+
from ansible.plugins.lookup import LookupBase
33+
34+
from cm_client import ApiClient, Configuration
35+
from cm_client.rest import ApiException, RESTClientObject
36+
37+
38+
__maintainer__ = ["[email protected]"]
39+
40+
41+
"""
42+
A common Ansible Lookup plugin for API access to Cloudera Manager.
43+
"""
44+
45+
class ClouderaManagerLookupBase(LookupBase):
46+
def initialize_client(self):
47+
# Set up core CM API client parameters
48+
config = Configuration()
49+
config.username = self.get_option("username")
50+
config.password = self.get_option("password")
51+
config.verify_ssl = self.get_option("verify_tls")
52+
config.debug = self.get_option("debug")
53+
54+
# Configure logging
55+
_log_format = (
56+
"%(asctime)s - %(threadName)s - %(name)s - %(levelname)s - %(message)s"
57+
)
58+
if self.get_option("debug"):
59+
self._setup_logger(logging.DEBUG, _log_format)
60+
self.logger.debug("CM API agent: %s", self.get_option("agent_header"))
61+
else:
62+
self._setup_logger(logging.ERROR, _log_format)
63+
64+
if self.get_option("verify_tls") is False:
65+
disable_warnings(InsecureRequestWarning)
66+
67+
# If provided a CM API endpoint URL, use it directly
68+
if self.get_option("endpoint"):
69+
config.host = self.get_option("endpoint")
70+
# Otherwise, run discovery on missing parts
71+
else:
72+
config.host = self._discover_endpoint(config)
73+
74+
self.api_client = ApiClient()
75+
76+
def _setup_logger(self, log_level, log_format):
77+
"""Configures the logging of the HTTP activity"""
78+
self.logger = logging.getLogger("urllib3")
79+
self.logger.setLevel(log_level)
80+
81+
def _get_auth_headers(self, config):
82+
"""Constructs a Basic Auth header dictionary from the Configuration.
83+
This dictionary can be used directly with the API client's REST client."""
84+
headers = dict()
85+
auth = config.auth_settings().get("basic")
86+
headers[auth["key"]] = auth["value"]
87+
return headers
88+
89+
def _discover_endpoint(self, config):
90+
"""Discovers the scheme and version of a potential Cloudara Manager host"""
91+
# Get the authentication headers and REST client
92+
headers = self._get_auth_headers(config)
93+
rest = RESTClientObject()
94+
95+
# Resolve redirects to establish HTTP scheme and port
96+
pre_rendered = Url(
97+
scheme="https" if self.get_option("force_tls") else "http",
98+
host=self.get_option("host"),
99+
port=self.get_option("port"),
100+
)
101+
rendered = rest.pool_manager.request(
102+
"GET", pre_rendered.url, headers=headers.copy()
103+
)
104+
rendered_url = rendered.geturl()
105+
106+
# Discover API version if not set
107+
if not self.version:
108+
pre_versioned = urljoin(rendered_url, "/api/version")
109+
versioned = rest.pool_manager.request("GET", pre_versioned, headers=headers)
110+
self.version = versioned.data.decode("utf-8")
111+
112+
# Construct the discovered API endpoint
113+
return urljoin(rendered_url, "/api/" + self.version)
114+
115+
def get(self, path, query=None, field="items", body=None):
116+
"""Wrapper to GET a CM API endpoint path directly."""
117+
path_params = []
118+
header_params = {}
119+
header_params["Accept"] = self.api_client.select_header_accept(
120+
["application/json"]
121+
)
122+
header_params["Content-Type"] = self.api_client.select_header_content_type(
123+
["application/json"]
124+
)
125+
126+
try:
127+
results = self.api_client.call_api(
128+
path,
129+
"GET",
130+
path_params,
131+
query,
132+
header_params,
133+
auth_settings=["basic"],
134+
_preload_content=False,
135+
)
136+
137+
if 200 >= results[1] <= 299:
138+
data = json.loads(results[0].data.decode("utf-8"))
139+
if field in data:
140+
data = data[field]
141+
return data if type(data) is list else [data]
142+
else:
143+
raise AnsibleError(
144+
"Error interacting with CM resource. Status code: %s"
145+
% to_text(results[1])
146+
)
147+
except ApiException as ae:
148+
body = ae.body.decode("utf-8")
149+
if body != "":
150+
body = json.loads(body)
151+
raise AnsibleError(
152+
"API error: %s; Status code: %s" % (ae.reason, ae.status),
153+
obj=body,
154+
orig_exc=ae,
155+
)
156+
except MaxRetryError as maxe:
157+
raise AnsibleError("Request error: %s" % to_text(maxe.reason))
158+
except HTTPError as he:
159+
raise AnsibleError("HTTP request error", orig_exc=he)

plugins/module_utils/cm_utils.py

Lines changed: 4 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# limitations under the License.
1717

1818
"""
19-
A common Ansible Module and Lookup for shared functions for Cloudera Manager
19+
A common Ansible Module functions for Cloudera Manager
2020
"""
2121

2222
import io
@@ -30,10 +30,6 @@
3030
from urllib.parse import urljoin
3131

3232
from ansible.module_utils.basic import AnsibleModule
33-
from ansible.errors import AnsibleError
34-
from ansible.module_utils.common.text.converters import to_text
35-
from ansible.plugins.lookup import LookupBase
36-
from ansible.utils.display import Display
3733

3834
from cm_client import ApiClient, Configuration
3935
from cm_client.rest import ApiException, RESTClientObject
@@ -43,8 +39,9 @@
4339
__credits__ = ["[email protected]"]
4440
__maintainer__ = ["[email protected]"]
4541

46-
display = Display()
47-
42+
"""
43+
A common Ansible Module for API access to Cloudera Manager.
44+
"""
4845

4946
class ClouderaManagerModule(object):
5047
@classmethod
@@ -276,125 +273,3 @@ def ansible_module(
276273
required_together=required_together,
277274
**kwargs,
278275
)
279-
280-
281-
"""
282-
A common Ansible Lookup plugin for API access to Cloudera Manager.
283-
"""
284-
285-
286-
class ClouderaManagerLookupBase(LookupBase):
287-
def initialize_client(self):
288-
# Set up core CM API client parameters
289-
config = Configuration()
290-
config.username = self.get_option("username")
291-
config.password = self.get_option("password")
292-
config.verify_ssl = self.get_option("verify_tls")
293-
config.debug = self.get_option("debug")
294-
295-
# Configure logging
296-
_log_format = (
297-
"%(asctime)s - %(threadName)s - %(name)s - %(levelname)s - %(message)s"
298-
)
299-
if self.get_option("debug"):
300-
self._setup_logger(logging.DEBUG, _log_format)
301-
self.logger.debug("CM API agent: %s", self.get_option("agent_header"))
302-
else:
303-
self._setup_logger(logging.ERROR, _log_format)
304-
305-
if self.get_option("verify_tls") is False:
306-
disable_warnings(InsecureRequestWarning)
307-
308-
# If provided a CM API endpoint URL, use it directly
309-
if self.get_option("endpoint"):
310-
config.host = self.get_option("endpoint")
311-
# Otherwise, run discovery on missing parts
312-
else:
313-
config.host = self._discover_endpoint(config)
314-
315-
self.api_client = ApiClient()
316-
317-
def _setup_logger(self, log_level, log_format):
318-
"""Configures the logging of the HTTP activity"""
319-
self.logger = logging.getLogger("urllib3")
320-
self.logger.setLevel(log_level)
321-
322-
def _get_auth_headers(self, config):
323-
"""Constructs a Basic Auth header dictionary from the Configuration.
324-
This dictionary can be used directly with the API client's REST client."""
325-
headers = dict()
326-
auth = config.auth_settings().get("basic")
327-
headers[auth["key"]] = auth["value"]
328-
return headers
329-
330-
def _discover_endpoint(self, config):
331-
"""Discovers the scheme and version of a potential Cloudara Manager host"""
332-
# Get the authentication headers and REST client
333-
headers = self._get_auth_headers(config)
334-
rest = RESTClientObject()
335-
336-
# Resolve redirects to establish HTTP scheme and port
337-
pre_rendered = Url(
338-
scheme="https" if self.get_option("force_tls") else "http",
339-
host=self.get_option("host"),
340-
port=self.get_option("port"),
341-
)
342-
rendered = rest.pool_manager.request(
343-
"GET", pre_rendered.url, headers=headers.copy()
344-
)
345-
rendered_url = rendered.geturl()
346-
347-
# Discover API version if not set
348-
if not self.version:
349-
pre_versioned = urljoin(rendered_url, "/api/version")
350-
versioned = rest.pool_manager.request("GET", pre_versioned, headers=headers)
351-
self.version = versioned.data.decode("utf-8")
352-
353-
# Construct the discovered API endpoint
354-
return urljoin(rendered_url, "/api/" + self.version)
355-
356-
def get(self, path, query=None, field="items", body=None):
357-
"""Wrapper to GET a CM API endpoint path directly."""
358-
path_params = []
359-
header_params = {}
360-
header_params["Accept"] = self.api_client.select_header_accept(
361-
["application/json"]
362-
)
363-
header_params["Content-Type"] = self.api_client.select_header_content_type(
364-
["application/json"]
365-
)
366-
367-
try:
368-
results = self.api_client.call_api(
369-
path,
370-
"GET",
371-
path_params,
372-
query,
373-
header_params,
374-
auth_settings=["basic"],
375-
_preload_content=False,
376-
)
377-
378-
if 200 >= results[1] <= 299:
379-
data = json.loads(results[0].data.decode("utf-8"))
380-
if field in data:
381-
data = data[field]
382-
return data if type(data) is list else [data]
383-
else:
384-
raise AnsibleError(
385-
"Error interacting with CM resource. Status code: %s"
386-
% to_text(results[1])
387-
)
388-
except ApiException as ae:
389-
body = ae.body.decode("utf-8")
390-
if body != "":
391-
body = json.loads(body)
392-
raise AnsibleError(
393-
"API error: %s; Status code: %s" % (ae.reason, ae.status),
394-
obj=body,
395-
orig_exc=ae,
396-
)
397-
except MaxRetryError as maxe:
398-
raise AnsibleError("Request error: %s" % to_text(maxe.reason))
399-
except HTTPError as he:
400-
raise AnsibleError("HTTP request error", orig_exc=he)

plugins/modules/cm_resource.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
# See the License for the specific language governing permissions and
1616
# limitations under the License.
1717

18-
import json
19-
2018
from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import ClouderaManagerModule
2119

2220
ANSIBLE_METADATA = {'metadata_version': '1.1',

0 commit comments

Comments
 (0)