Skip to content

Commit dd8a679

Browse files
authored
Add Host Template modules (cloudera-labs#238)
Signed-off-by: rsuplina <[email protected]>
1 parent 17bf83e commit dd8a679

File tree

5 files changed

+665
-0
lines changed

5 files changed

+665
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright 2024 Cloudera, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distribuFd under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
A common functions for Cloudera Manager host templates
17+
"""
18+
19+
HOST_TEMPLATE_OUTPUT = ["name", "cluster_ref", "role_config_group_refs"]
20+
21+
22+
def _parse_host_template_output(host_template: dict) -> dict:
23+
result = _parse_output(host_template, HOST_TEMPLATE_OUTPUT)
24+
result["cluster_name"] = result["cluster_ref"]["cluster_name"]
25+
result["role_groups"] = [
26+
role["role_config_group_name"] for role in result["role_config_group_refs"]
27+
]
28+
del result["cluster_ref"]
29+
del result["role_config_group_refs"]
30+
return result
31+
32+
33+
def _parse_host_templates_output(host_templates: list) -> list:
34+
parsed_templates = [template.to_dict() for template in host_templates]
35+
return [
36+
_parse_host_template_output(template_dict) for template_dict in parsed_templates
37+
]
38+
39+
40+
def _parse_output(host_template: dict, keys: list) -> dict:
41+
return {key: host_template[key] for key in keys if key in host_template}

plugins/modules/host_template.py

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
# Copyright 2024 Cloudera, Inc. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import (
16+
ClouderaManagerModule,
17+
)
18+
from ansible_collections.cloudera.cluster.plugins.module_utils.host_template_utils import (
19+
_parse_host_template_output,
20+
)
21+
from cm_client import (
22+
HostTemplatesResourceApi,
23+
ClustersResourceApi,
24+
ApiHostTemplate,
25+
ApiRoleConfigGroupRef,
26+
ApiClusterRef,
27+
ApiHostTemplateList,
28+
)
29+
from cm_client.rest import ApiException
30+
31+
ANSIBLE_METADATA = {
32+
"metadata_version": "1.1",
33+
"status": ["preview"],
34+
"supported_by": "community",
35+
}
36+
37+
DOCUMENTATION = r"""
38+
---
39+
module: host_template
40+
short_description: Configure a host template
41+
description:
42+
- Creates a new host template or updates an existing one
43+
- The module supports C(check_mode).
44+
author:
45+
- "Ronald Suplina (@rsuplina)"
46+
requirements:
47+
- cm_client
48+
options:
49+
cluster:
50+
description:
51+
- The associated cluster name.
52+
type: str
53+
required: yes
54+
aliases:
55+
- cluster_name
56+
name:
57+
description:
58+
- The name of the host template.
59+
type: str
60+
required: yes
61+
role_groups:
62+
description:
63+
- Names of the role configuration groups associated with the host template.
64+
type: list
65+
returned: yes
66+
aliases:
67+
- role_config_groups
68+
attributes:
69+
check_mode:
70+
support: full
71+
diff_mode:
72+
support: full
73+
"""
74+
75+
EXAMPLES = r"""
76+
---
77+
- name: Create host template
78+
cloudera.cluster.host_template
79+
host: example.cloudera.com
80+
username: "jane_smith"
81+
password: "S&peR4Ec*re"
82+
cluster: "base_cluster"
83+
name: "MyTemplate"
84+
role_groups: ["kafka-GATEWAY-BASE", "atlas-ATLAS_SERVER-BASE" , "hive_on_tez-GATEWAY-BASE"]
85+
86+
- name: Update host template
87+
cloudera.cluster.host_template
88+
host: example.cloudera.com
89+
username: "jane_smith"
90+
password: "S&peR4Ec*re"
91+
cluster: "base_cluster"
92+
name: "MyTemplate"
93+
role_groups: ["kafka-GATEWAY-BASE", "atlas-ATLAS_SERVER-BASE"]
94+
95+
- name: Remove host template
96+
cloudera.cluster.host_template
97+
host: example.cloudera.com
98+
username: "jane_smith"
99+
password: "S&peR4Ec*re"
100+
cluster: "base_cluster"
101+
name: "MyTemplate"
102+
state: "absent"
103+
"""
104+
105+
RETURN = r"""
106+
---
107+
host_template:
108+
description:
109+
- Retrieve details about host template.
110+
type: dict
111+
elements: dict
112+
returned: always
113+
contains:
114+
name:
115+
description:
116+
- The name of the host template
117+
type: str
118+
returned: always
119+
cluster_name:
120+
description: A reference to the enclosing cluster.
121+
type: str
122+
returned: always
123+
role_groups:
124+
description:
125+
- The role config groups belonging to this host tempalte.
126+
type: list
127+
returned: always
128+
"""
129+
130+
131+
class ClouderaHostTemplate(ClouderaManagerModule):
132+
def __init__(self, module):
133+
super(ClouderaHostTemplate, self).__init__(module)
134+
135+
# Set the parameters
136+
self.cluster_name = self.get_param("cluster")
137+
self.name = self.get_param("name")
138+
self.role_groups = self.get_param("role_groups")
139+
self.state = self.get_param("state")
140+
141+
# Initialize the return value
142+
self.host_template = []
143+
self.host_template_output = []
144+
self.changed = False
145+
self.diff = {}
146+
147+
# Execute the logic
148+
self.process()
149+
150+
@ClouderaManagerModule.handle_process
151+
def process(self):
152+
host_temp_api_instance = HostTemplatesResourceApi(self.api_client)
153+
try:
154+
ClustersResourceApi(self.api_client).read_cluster(self.cluster_name)
155+
except ApiException as ex:
156+
if ex.status == 404:
157+
self.module.fail_json(
158+
msg="Cluster does not exist: " + self.cluster_name
159+
)
160+
else:
161+
raise ex
162+
try:
163+
self.host_template = host_temp_api_instance.read_host_template(
164+
cluster_name=self.cluster_name,
165+
host_template_name=self.name,
166+
)
167+
except ApiException as ex:
168+
if ex.status == 404:
169+
pass
170+
else:
171+
raise ex
172+
173+
if self.host_template:
174+
if self.module._diff:
175+
current = {
176+
item.role_config_group_name
177+
for item in self.host_template.role_config_group_refs
178+
}
179+
incoming = set(self.role_groups)
180+
self.diff.update(
181+
before=list(current - incoming), after=list(incoming - current)
182+
)
183+
184+
if self.state == "present":
185+
host_template_body = ApiHostTemplate(
186+
cluster_ref=ApiClusterRef(
187+
cluster_name=self.cluster_name, display_name=self.cluster_name
188+
),
189+
name=self.name,
190+
role_config_group_refs=[
191+
ApiRoleConfigGroupRef(role_config_group_name=group)
192+
for group in self.role_groups
193+
],
194+
)
195+
if self.host_template:
196+
if not self.module.check_mode:
197+
host_temp_api_instance.update_host_template(
198+
cluster_name=self.cluster_name,
199+
host_template_name=self.name,
200+
body=host_template_body,
201+
)
202+
self.changed = True
203+
else:
204+
body = ApiHostTemplateList(items=[host_template_body])
205+
if not self.module.check_mode:
206+
host_temp_api_instance.create_host_templates(
207+
cluster_name=self.cluster_name, body=body
208+
)
209+
self.changed = True
210+
211+
self.host_template_output = _parse_host_template_output(
212+
host_temp_api_instance.read_host_template(
213+
cluster_name=self.cluster_name,
214+
host_template_name=self.name,
215+
).to_dict()
216+
)
217+
218+
if self.state == "absent":
219+
if not self.module.check_mode:
220+
self.host_template_output = _parse_host_template_output(
221+
host_temp_api_instance.delete_host_template(
222+
cluster_name=self.cluster_name,
223+
host_template_name=self.name,
224+
).to_dict()
225+
)
226+
self.changed = True
227+
228+
229+
def main():
230+
module = ClouderaManagerModule.ansible_module(
231+
argument_spec=dict(
232+
cluster=dict(required=True, type="str", aliases=["cluster_name"]),
233+
name=dict(required=True, type="str"),
234+
role_groups=dict(
235+
required=False, type="list", aliases=["role_config_groups"]
236+
),
237+
state=dict(
238+
type="str",
239+
default="present",
240+
choices=["present", "absent"],
241+
),
242+
),
243+
supports_check_mode=True,
244+
required_if=[
245+
("state", "present", ("cluster", "role_groups")),
246+
],
247+
)
248+
249+
result = ClouderaHostTemplate(module)
250+
251+
output = dict(
252+
changed=result.changed,
253+
host_template_output=result.host_template_output,
254+
)
255+
if module._diff:
256+
output.update(diff=result.diff)
257+
258+
if result.debug:
259+
log = result.log_capture.getvalue()
260+
output.update(debug=log, debug_lines=log.split("\n"))
261+
262+
module.exit_json(**output)
263+
264+
265+
if __name__ == "__main__":
266+
main()

0 commit comments

Comments
 (0)