From c92c3138dc0ee7cc2fc843b47099c0bd4f684cda Mon Sep 17 00:00:00 2001 From: rsuplina Date: Thu, 18 Jan 2024 12:08:14 +0000 Subject: [PATCH] Add Import_cluster_template module Signed-off-by: rsuplina --- plugins/modules/cm_import_cluster_template.py | 171 ++++++++++++++++++ .../test_cm_import_cluster_template.py | 46 +++++ 2 files changed, 217 insertions(+) create mode 100644 plugins/modules/cm_import_cluster_template.py create mode 100644 tests/unit/plugins/modules/cm_import_cluster_template/test_cm_import_cluster_template.py diff --git a/plugins/modules/cm_import_cluster_template.py b/plugins/modules/cm_import_cluster_template.py new file mode 100644 index 00000000..b0465811 --- /dev/null +++ b/plugins/modules/cm_import_cluster_template.py @@ -0,0 +1,171 @@ +# 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 ClouderaManagerResourceApi +from cm_client import ClustersResourceApi +import json + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = r""" +--- +module: cm_import_cluster_template +short_description: Create a cluster based on the provided cluster template +description: + - Searches for a template file. + - The search for the file starts at the root directory where the Ansible playbook is executed. By default, the template is expected to be placed inside the './files' directory. + - Imports the template file and uses it to create the cluster. + - This module ensures that the cluster is created according to the specified template. +author: + - "Ronald Suplina (@rsuplina)" +requirements: + - cm_client +""" + +EXAMPLES = r""" +--- +- name: Create a cluster on Cloudera Manager host + cloudera.cluster.cm_import_cluster_template: + host: example.cloudera.com + username: "jane_smith" + password: "S&peR4Ec*re" + port: "7180" + template: "./files/cluster-template.json" +""" + +RETURN = r""" +--- +cloudera_manager: + description: Details about Cloudera Manager Cluster + type: dict + contains: + cluster_type: + description: The type of cluster created from template. + 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 ClusterTemplate(ClouderaManagerModule): + def __init__(self, module): + super(ClusterTemplate, self).__init__(module) + self.template = self.get_param("template") + self.process() + + @ClouderaManagerModule.handle_process + def process(self): + + try: + api_instance = ClouderaManagerResourceApi(self.api_client) + cluster_api_instance = ClustersResourceApi(self.api_client) + + with open(self.template, 'r') as file: + template_json = json.load(file) + + import_template_request = api_instance.import_cluster_template(body=template_json).to_dict() + command_id = import_template_request['id'] + + self.wait_for_command_state(command_id=command_id,polling_interval=60) + + self.cm_cluster_template_output = cluster_api_instance.read_clusters().to_dict() + self.changed = True + self.file_not_found = False + + except ApiException as e: + if e.status == 400: + self.cm_cluster_template_output = json.loads(e.body) + self.changed = False + self.file_not_found = False + + except FileNotFoundError: + self.cm_cluster_template_output = (f"Error: File '{self.template}' not found.") + self.file_not_found = True +def main(): + module = ClouderaManagerModule.ansible_module( + + argument_spec=dict( + template=dict(required=True, type="path"), + ), + supports_check_mode=False + ) + + result = ClusterTemplate(module) + + if result.file_not_found: + module.fail_json(msg=str(result.cm_cluster_template_output)) + + changed = result.changed + + output = dict( + changed=changed, + cloudera_manager=result.cm_cluster_template_output, + ) + + 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_import_cluster_template/test_cm_import_cluster_template.py b/tests/unit/plugins/modules/cm_import_cluster_template/test_cm_import_cluster_template.py new file mode 100644 index 00000000..27e22609 --- /dev/null +++ b/tests/unit/plugins/modules/cm_import_cluster_template/test_cm_import_cluster_template.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +# Copyright 2023 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_import_cluster_template +from ansible_collections.cloudera.cluster.tests.unit import AnsibleExitJson, AnsibleFailJson + +LOG = logging.getLogger(__name__) + +def test_pytest_cm_import_cluster_template(module_args): + module_args( + { + "username": os.getenv('CM_USERNAME'), + "password": os.getenv('CM_PASSWORD'), + "host": os.getenv('CM_HOST'), + "port": "7180", + "verify_tls": "no", + "debug": "no", + "template": "./files/cluster-template.json", + } + ) + + with pytest.raises(AnsibleExitJson) as e: + cm_import_cluster_template.main() + + # LOG.info(str(e.value)) + LOG.info(str(e.value.cloudera_manager))