Skip to content

Commit 8725267

Browse files
authored
Add module to query Cloudera support matrix (#246)
Signed-off-by: Jim Enright <[email protected]>
1 parent 7ccee73 commit 8725267

File tree

1 file changed

+352
-0
lines changed

1 file changed

+352
-0
lines changed

plugins/modules/supported.py

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright 2025 Cloudera, Inc. All Rights Reserved.
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+
DOCUMENTATION = r"""
19+
module: supported
20+
short_description: Retrieve Cloudera Support Matrix information
21+
description:
22+
- Retrieve product compatibility and support information from the Cloudera Support Matrix API.
23+
- Supports filtering by Cloudera Runtime, Manager and Data Service product versions.
24+
- Returns comprehensive support matrix data including compatibility information across different Cloudera products.
25+
author:
26+
- "Jim Enright (@jimright)"
27+
version_added: "3.0.0"
28+
options:
29+
cloudera_manager_version:
30+
description:
31+
- Filter by specific Cloudera Manager version.
32+
- Mutually exclusive with the O(cloudera_runtime_version) and O(cloudera_data_services_version) parameters.
33+
type: str
34+
required: false
35+
cloudera_runtime_version:
36+
description:
37+
- Filter by specific CDP Private Cloud Base (Cloudera Runtime) version.
38+
- Mutually exclusive with the O(cloudera_manager_version) and O(cloudera_data_services_version) parameters.
39+
type: str
40+
required: false
41+
cloudera_data_services_version:
42+
description:
43+
- Filter by specific CDP Private Cloud Data Services version.
44+
- Mutually exclusive with the O(cloudera_manager_version) and O(cloudera_runtime_version) parameters.
45+
type: str
46+
required: false
47+
timeout:
48+
description:
49+
- HTTP request timeout in seconds.
50+
type: int
51+
default: 30
52+
notes:
53+
- Only one of the O(cloudera_manager_version), O(cloudera_runtime_version) or O(cloudera_data_services_version) parameters can be specified.
54+
"""
55+
56+
EXAMPLES = r"""
57+
- name: Get all support matrix information
58+
cloudera.exe.supported:
59+
register: full_matrix
60+
61+
- name: Get support matrix for Cloudera Manager version
62+
cloudera.exe.supported:
63+
cloudera_manager_version: "7.13.1"
64+
register: cm_support
65+
66+
- name: Get support matrix for Cloudera Runtime version
67+
cloudera.exe.supported:
68+
cloudera_runtime_version: "7.1.9 SP1"
69+
register: base_support
70+
71+
- name: Get support matrix for Cloudera Data Services version
72+
cloudera.exe.supported:
73+
cloudera_data_services_version: "1.5.4"
74+
register: ds_support
75+
"""
76+
77+
RETURN = r"""
78+
support_matrix_data:
79+
description: Complete support matrix information from Cloudera
80+
type: dict
81+
returned: always
82+
contains:
83+
browsers:
84+
description: Browser support information
85+
type: list
86+
returned: always
87+
databases:
88+
description: Database compatibility information
89+
type: list
90+
returned: always
91+
sample: [
92+
{
93+
"version": "13",
94+
"family": "PostgreSQL",
95+
"description": "PostgreSQL-13",
96+
"id": 801
97+
}
98+
]
99+
jdks:
100+
description: JDK compatibility information
101+
type: list
102+
returned: always
103+
sample: [
104+
{
105+
"version": "JDK11",
106+
"family": "OpenJDK",
107+
"description": "OpenJDK-JDK11",
108+
"id": 797
109+
}
110+
]
111+
products:
112+
description: Cloudera product information
113+
type: list
114+
returned: always
115+
sample: [
116+
{
117+
"version": "7.13.1",
118+
"productName": "Cloudera Manager",
119+
"description": "Cloudera Manager-7.13.1",
120+
"id": 1325,
121+
"group": "7.13"
122+
}
123+
]
124+
kubernetes:
125+
description: Kubernetes platform compatibility
126+
type: list
127+
returned: always
128+
processor:
129+
description: Processor architecture compatibility
130+
type: list
131+
returned: always
132+
operatingSystems:
133+
description: Operating system compatibility
134+
type: list
135+
returned: always
136+
sample: [
137+
{
138+
"version": "9.4",
139+
"family": "Rocky Linux",
140+
"description": "Rocky Linux-9.4",
141+
"id": 1087,
142+
"group": "9"
143+
}
144+
]
145+
filters_applied:
146+
description: Summary of filters that were applied to the API request
147+
type: dict
148+
returned: always
149+
contains:
150+
cloudera_manager_version:
151+
description: Cloudera Manager version filter applied
152+
type: str
153+
returned: when filter applied
154+
cloudera_runtime_version:
155+
description: CDP Private Cloud Base version filter applied
156+
type: str
157+
returned: when filter applied
158+
cloudera_data_services_version:
159+
description: CDP Private Cloud Data Services version filter applied
160+
type: str
161+
returned: when filter applied
162+
"""
163+
164+
import json
165+
from ansible.module_utils.basic import AnsibleModule
166+
from ansible.module_utils.common.text.converters import to_native
167+
from ansible.module_utils.urls import fetch_url, to_text
168+
from urllib.parse import quote
169+
170+
171+
class ClouderaSupportMatrix:
172+
"""
173+
Class to handle Cloudera Support Matrix API interactions.
174+
175+
This class manages the construction of API requests, filtering parameters,
176+
and processing of responses from the Cloudera Support Matrix API.
177+
"""
178+
179+
BASE_URL = "https://supportmatrix.cloudera.com/supportmatrices/cldr"
180+
181+
# Mapping of parameter names to actual product names in the API
182+
PRODUCT_NAME_MAPPING = {
183+
"cloudera_manager_version": "Cloudera Manager",
184+
"cloudera_runtime_version": "CDP Private Cloud Base",
185+
"cloudera_data_services_version": "CDP Private Cloud Data Services",
186+
}
187+
188+
def __init__(self, module):
189+
"""
190+
Initialize the ClouderaSupportMatrix class.
191+
192+
Args:
193+
module: AnsibleModule instance
194+
"""
195+
self.module = module
196+
197+
# Extract product version parameters
198+
self.product_versions = {}
199+
for param_name in self.PRODUCT_NAME_MAPPING.keys():
200+
version = module.params.get(param_name)
201+
if version:
202+
self.product_versions[param_name] = version
203+
204+
self.timeout = module.params.get("timeout", 30)
205+
206+
# Initialize return values
207+
self.support_matrix_data = {}
208+
self.filters_applied = {}
209+
210+
# Execute the logic
211+
self.process()
212+
213+
def _build_query_conditions(self):
214+
"""
215+
Build query conditions for API filtering.
216+
217+
Returns:
218+
str: Query conditions string for the API request
219+
"""
220+
conditions = []
221+
222+
# Build single PRODUCT condition with comma-separated values
223+
if self.product_versions:
224+
products = []
225+
for param_name, version in self.product_versions.items():
226+
product_name = self.PRODUCT_NAME_MAPPING[param_name]
227+
# URL encode spaces in both product name and version
228+
# Use urllib.parse.quote for proper URL encoding
229+
product_version_string = f"{product_name}-{version}"
230+
encoded_product_version = quote(product_version_string)
231+
products.append(encoded_product_version)
232+
self.filters_applied[param_name] = version
233+
234+
if products:
235+
# Join products with commas for a single PRODUCT parameter
236+
product_condition = f"PRODUCT={','.join(products)}"
237+
conditions.append(product_condition)
238+
239+
return ";".join(conditions)
240+
241+
def _build_api_url(self):
242+
"""
243+
Build the complete API URL with query parameters.
244+
245+
Returns:
246+
str: Complete API URL
247+
"""
248+
conditions = self._build_query_conditions()
249+
250+
if conditions:
251+
# Add trailing semicolon as shown in the curl example
252+
# Don't URL encode the entire condition string, only spaces are encoded
253+
api_url = f"{self.BASE_URL}?condition={conditions};"
254+
else:
255+
api_url = self.BASE_URL
256+
257+
return api_url
258+
259+
def process(self):
260+
"""
261+
Fetch support matrix data from the Cloudera API using Ansible's fetch_url.
262+
"""
263+
264+
try:
265+
# Build the API URL
266+
api_url = self._build_api_url()
267+
268+
# Prepare headers
269+
headers = {
270+
"Accept": "*/*",
271+
"Content-Type": "application/json",
272+
"User-Agent": "Ansible/cloudera.cluster",
273+
}
274+
275+
# Use Ansible's fetch_url
276+
response, info = fetch_url(
277+
self.module,
278+
api_url,
279+
headers=headers,
280+
timeout=self.timeout,
281+
)
282+
283+
if info.get("status") != 200:
284+
self.module.fail_json(
285+
msg=f"HTTP error occurred: {info.get('msg', 'Unknown error')}",
286+
api_url=api_url,
287+
http_status=info.get("status"),
288+
http_reason=info.get("msg"),
289+
)
290+
291+
if response:
292+
response_text = to_text(response.read())
293+
else:
294+
self.module.fail_json(
295+
msg="Empty response received from API",
296+
api_url=api_url,
297+
)
298+
299+
# Parse JSON response
300+
try:
301+
self.support_matrix_data = json.loads(response_text)
302+
except json.JSONDecodeError as e:
303+
self.module.fail_json(
304+
msg=f"Failed to parse JSON response: {to_native(e)}",
305+
api_url=api_url,
306+
response_text=response_text,
307+
)
308+
309+
except Exception as e:
310+
self.module.fail_json(
311+
msg=f"Unexpected error occurred: {to_native(e)}",
312+
api_url=api_url,
313+
)
314+
315+
316+
def main():
317+
"""
318+
Main function to execute the Ansible module.
319+
"""
320+
321+
# Define module arguments
322+
module = AnsibleModule(
323+
argument_spec=dict(
324+
cloudera_manager_version=dict(type="str", required=False),
325+
cloudera_runtime_version=dict(type="str", required=False),
326+
cloudera_data_services_version=dict(type="str", required=False),
327+
timeout=dict(type="int", default=30),
328+
),
329+
mutually_exclusive=[
330+
(
331+
"cloudera_manager_version",
332+
"cloudera_runtime_version",
333+
"cloudera_data_services_version",
334+
),
335+
],
336+
)
337+
338+
# Create and Fetch support matrix
339+
result = ClouderaSupportMatrix(module)
340+
341+
# Prepare successful response
342+
output = dict(
343+
changed=False,
344+
support_matrix_data=result.support_matrix_data,
345+
filters_applied=result.filters_applied,
346+
)
347+
348+
module.exit_json(**output)
349+
350+
351+
if __name__ == "__main__":
352+
main()

0 commit comments

Comments
 (0)