From 0b44833f8c24b9681ba3615adafad690164deb14 Mon Sep 17 00:00:00 2001 From: rsuplina Date: Mon, 4 Mar 2024 12:47:30 +0000 Subject: [PATCH 1/3] Add Cluster Info Module Signed-off-by: rsuplina --- plugins/modules/cm_cluster_info.py | 153 ++++++++++++++++++ .../cm_cluster_info/test_cm_cluster_info.py | 43 +++++ 2 files changed, 196 insertions(+) create mode 100644 plugins/modules/cm_cluster_info.py create mode 100644 tests/unit/plugins/modules/cm_cluster_info/test_cm_cluster_info.py diff --git a/plugins/modules/cm_cluster_info.py b/plugins/modules/cm_cluster_info.py new file mode 100644 index 00000000..4a989a23 --- /dev/null +++ b/plugins/modules/cm_cluster_info.py @@ -0,0 +1,153 @@ +# Copyright 2024 Cloudera, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import ( + ClouderaManagerModule, +) +from cm_client.rest import ApiException +from cm_client import ClustersResourceApi + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = r""" +--- +module: cm_cluster_info +short_description: Retrieve information about a cluster based on the provided cluster name +description: + - Module checks the existence of a cluster with the specified name and retrieves detailed information about the cluster. +author: + - "Ronald Suplina (@rsuplina)" +options: + cluster_name: + description: + - Name of the Cloudera Manager cluster + type: str + required: True +requirements: + - cm_client +""" + +EXAMPLES = r""" +--- +- name: Get information about the cluster + cloudera.cluster.cm_cluster_info: + host: example.cloudera.com + username: "jane_smith" + cluster_name: "OneNodeCluster" + password: "S&peR4Ec*re" + port: "7180" + +""" + +RETURN = r""" +--- +cloudera_manager: + description: Details about Cloudera Manager Cluster + type: dict + contains: + cluster_type: + description: The type of Cloudera Manager cluster. + type: str + returned: optional + cluster_url: + description: Url of Cloudera Manager cluster. + type: str + returned: optional + display_name: + description: The name of the cluster displayed on the site. + type: str + returned: optional + entity_status: + description: Health status of the cluster. + type: str + returned: optional + full_version: + description: Version of the cluster installed. + type: str + returned: optional + hosts_url: + description: Url of all the hosts on which cluster is installed. + type: str + returned: optional + maintenance_mode: + description: Maintance mode of Cloudera Manager Cluster. + type: bool + returned: optional + maintenance_owners: + description: List of Maintance owners for Cloudera Manager Cluster. + type: list + returned: optional + name: + description: The name of the cluster created. + type: str + returned: optional + tags: + description: List of tags for Cloudera Manager Cluster. + type: list + returned: optional + uuid: + description: Unique ID of created cluster + type: bool + returned: optional +""" + + +class ClusterInfo(ClouderaManagerModule): + def __init__(self, module): + super(ClusterInfo, self).__init__(module) + self.cluster_name = self.get_param("cluster_name") + self.process() + + @ClouderaManagerModule.handle_process + def process(self): + try: + cluster_api_instance = ClustersResourceApi(self.api_client) + self.cm_cluster_info = cluster_api_instance.read_cluster(cluster_name=self.cluster_name).to_dict() + + except ApiException as e: + if e.status == 404: + self.cm_cluster_info = (f"Error: Cluster '{self.cluster_name}' not found.") + self.module.fail_json(msg=str(self.cm_cluster_info)) + +def main(): + module = ClouderaManagerModule.ansible_module( + + argument_spec=dict( + cluster_name=dict(required=True, type="str"), + ), + supports_check_mode=False + ) + + result = ClusterInfo(module) + + + + output = dict( + changed=False, + cloudera_manager=result.cm_cluster_info, + ) + + if result.debug: + log = result.log_capture.getvalue() + output.update(debug=log, debug_lines=log.split("\n")) + + module.exit_json(**output) + + +if __name__ == "__main__": + main() diff --git a/tests/unit/plugins/modules/cm_cluster_info/test_cm_cluster_info.py b/tests/unit/plugins/modules/cm_cluster_info/test_cm_cluster_info.py new file mode 100644 index 00000000..d09ba17b --- /dev/null +++ b/tests/unit/plugins/modules/cm_cluster_info/test_cm_cluster_info.py @@ -0,0 +1,43 @@ +# Copyright 2024 Cloudera, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +import os +import logging +import pytest + +from ansible_collections.cloudera.cluster.plugins.modules import cm_cluster_info +from ansible_collections.cloudera.cluster.tests.unit import AnsibleExitJson, AnsibleFailJson + +LOG = logging.getLogger(__name__) + +def test_pytest_cm_cluster_info(module_args): + module_args( + { + "username": os.getenv('CM_USERNAME'), + "password": os.getenv('CM_PASSWORD'), + "host": os.getenv('CM_HOST'), + "port": "7180", + "verify_tls": "no", + "cluster_name": "OneNodeCluster", + "debug": "no", + } + ) + + with pytest.raises(AnsibleExitJson) as e: + cm_cluster_info.main() + + LOG.info(str(e.value.cloudera_manager)) From a602f57438e6579fa04d67766599729d25523dfa Mon Sep 17 00:00:00 2001 From: rsuplina Date: Wed, 6 Mar 2024 10:07:49 +0000 Subject: [PATCH 2/3] Add requested changes Signed-off-by: rsuplina --- plugins/modules/cm_cluster_info.py | 41 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/plugins/modules/cm_cluster_info.py b/plugins/modules/cm_cluster_info.py index 4a989a23..fe177a7d 100644 --- a/plugins/modules/cm_cluster_info.py +++ b/plugins/modules/cm_cluster_info.py @@ -33,9 +33,10 @@ author: - "Ronald Suplina (@rsuplina)" options: - cluster_name: + name: description: - - Name of the Cloudera Manager cluster + - Name of Cloudera Manager cluster. + - This parameter specifies the name of the cluster from which data will be gathered. type: str required: True requirements: @@ -48,7 +49,7 @@ cloudera.cluster.cm_cluster_info: host: example.cloudera.com username: "jane_smith" - cluster_name: "OneNodeCluster" + name: "OneNodeCluster" password: "S&peR4Ec*re" port: "7180" @@ -63,72 +64,72 @@ cluster_type: description: The type of Cloudera Manager cluster. type: str - returned: optional + returned: always cluster_url: description: Url of Cloudera Manager cluster. type: str - returned: optional + returned: always display_name: description: The name of the cluster displayed on the site. type: str - returned: optional + returned: always entity_status: description: Health status of the cluster. type: str - returned: optional + returned: always full_version: description: Version of the cluster installed. type: str - returned: optional + returned: always hosts_url: description: Url of all the hosts on which cluster is installed. type: str - returned: optional + returned: always maintenance_mode: description: Maintance mode of Cloudera Manager Cluster. type: bool - returned: optional + returned: always maintenance_owners: description: List of Maintance owners for Cloudera Manager Cluster. type: list - returned: optional + returned: always name: - description: The name of the cluster created. + description: The name of the cluster. type: str - returned: optional + returned: always tags: description: List of tags for Cloudera Manager Cluster. type: list - returned: optional + returned: always uuid: - description: Unique ID of created cluster + description: Unique ID of the cluster type: bool - returned: optional + returned: always """ class ClusterInfo(ClouderaManagerModule): def __init__(self, module): super(ClusterInfo, self).__init__(module) - self.cluster_name = self.get_param("cluster_name") + self.name = self.get_param("name") self.process() @ClouderaManagerModule.handle_process def process(self): try: cluster_api_instance = ClustersResourceApi(self.api_client) - self.cm_cluster_info = cluster_api_instance.read_cluster(cluster_name=self.cluster_name).to_dict() + self.cm_cluster_info = cluster_api_instance.read_cluster(cluster_name=self.name).to_dict() except ApiException as e: if e.status == 404: - self.cm_cluster_info = (f"Error: Cluster '{self.cluster_name}' not found.") + self.cm_cluster_info = (f"Error: Cluster '{self.name}' not found.") self.module.fail_json(msg=str(self.cm_cluster_info)) def main(): module = ClouderaManagerModule.ansible_module( argument_spec=dict( - cluster_name=dict(required=True, type="str"), + name=dict(required=True, type="str", aliases=["cluster_name","cluster"]), ), supports_check_mode=False ) From 9855cc04ca1d9fff3f89622a6e5d9098cacc55cb Mon Sep 17 00:00:00 2001 From: rsuplina Date: Wed, 6 Mar 2024 15:03:11 +0000 Subject: [PATCH 3/3] Add refactoring changes Signed-off-by: rsuplina --- plugins/modules/cm_cluster_info.py | 43 ++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/plugins/modules/cm_cluster_info.py b/plugins/modules/cm_cluster_info.py index fe177a7d..c71a3b1a 100644 --- a/plugins/modules/cm_cluster_info.py +++ b/plugins/modules/cm_cluster_info.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from ansible.module_utils.basic import to_native from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import ( ClouderaManagerModule, ) @@ -27,9 +28,9 @@ DOCUMENTATION = r""" --- module: cm_cluster_info -short_description: Retrieve information about a cluster based on the provided cluster name +short_description: Retrieve details about one or more clusters description: - - Module checks the existence of a cluster with the specified name and retrieves detailed information about the cluster. + - Retrieves details about one or more clusters managed by Cloudera Manager author: - "Ronald Suplina (@rsuplina)" options: @@ -38,7 +39,7 @@ - Name of Cloudera Manager cluster. - This parameter specifies the name of the cluster from which data will be gathered. type: str - required: True + required: False requirements: - cm_client """ @@ -52,14 +53,22 @@ name: "OneNodeCluster" password: "S&peR4Ec*re" port: "7180" + +- name: Get information about all clusters + cloudera.cluster.cm_cluster_info: + host: example.cloudera.com + username: "jane_smith" + password: "S&peR4Ec*re" + port: "7180" """ RETURN = r""" --- -cloudera_manager: +clusters: description: Details about Cloudera Manager Cluster - type: dict + type: list + elements: dict contains: cluster_type: description: The type of Cloudera Manager cluster. @@ -112,35 +121,41 @@ class ClusterInfo(ClouderaManagerModule): def __init__(self, module): super(ClusterInfo, self).__init__(module) self.name = self.get_param("name") + self.output = [] self.process() @ClouderaManagerModule.handle_process def process(self): try: cluster_api_instance = ClustersResourceApi(self.api_client) - self.cm_cluster_info = cluster_api_instance.read_cluster(cluster_name=self.name).to_dict() - + if self.name: + self.output = [cluster_api_instance.read_cluster(cluster_name=self.name).to_dict()] + else: + self.output = cluster_api_instance.read_clusters().to_dict()['items'] + except ApiException as e: if e.status == 404: - self.cm_cluster_info = (f"Error: Cluster '{self.name}' not found.") - self.module.fail_json(msg=str(self.cm_cluster_info)) + pass + else: + raise e + except KeyError as ke: + self.module.fail_json(msg='Invalid result object from Cloudera Manager API', error=to_native(ke)) + def main(): module = ClouderaManagerModule.ansible_module( argument_spec=dict( - name=dict(required=True, type="str", aliases=["cluster_name","cluster"]), + name=dict(aliases=["cluster_name","cluster"]), ), - supports_check_mode=False + supports_check_mode=True ) result = ClusterInfo(module) - - output = dict( changed=False, - cloudera_manager=result.cm_cluster_info, + clusters=result.output, ) if result.debug: