From 9128c8ad7b6a2db0b6c50c6ba896af4b4019cd9b Mon Sep 17 00:00:00 2001 From: rsuplina Date: Fri, 19 Jul 2024 11:27:58 +0100 Subject: [PATCH 1/5] Add Data Context module Signed-off-by: rsuplina --- plugins/modules/data_context.py | 316 ++++++++++++++++++ plugins/modules/data_context_info.py | 200 +++++++++++ .../modules/data_contex/test_data_contex.py | 98 ++++++ .../test_data_context_info.py | 82 +++++ 4 files changed, 696 insertions(+) create mode 100644 plugins/modules/data_context.py create mode 100644 plugins/modules/data_context_info.py create mode 100644 tests/unit/plugins/modules/data_contex/test_data_contex.py create mode 100644 tests/unit/plugins/modules/data_contex_info/test_data_context_info.py diff --git a/plugins/modules/data_context.py b/plugins/modules/data_context.py new file mode 100644 index 00000000..fe6278e4 --- /dev/null +++ b/plugins/modules/data_context.py @@ -0,0 +1,316 @@ +# 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 ( + ClouderaManagerMutableModule, +) +from cm_client import DataContextsResourceApi, ApiDataContextRef, ApiDataContextList + +from cm_client import ( + ClustersResourceApi, + ApiDataContext, +) +from cm_client.rest import ApiException +from ansible_collections.cloudera.cluster.plugins.module_utils.data_context_utils import ( + parse_data_context_result, +) + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = r""" +--- +module: data_context +short_description: Create, update, or delete a data context +description: + - Configure details of a specific data context. + - Create a new data context. + - Update an existing data context. + - Delete a data context. + - The module supports C(check_mode). +author: + - "Ronald Suplina (@rsuplina)" +requirements: + - cm_client +options: + name: + description: + - The name of the data context. + type: str + required: yes + cluster: + description: + - The name of the Cloudera Manager cluster. + type: str + required: no + services: + description: + - A list of services that the data context will include. + type: list + required: no + state: + description: + - If I(state=present), the data context will be created or updated. + - If I(state=absent), the data context will be deleted deleted. + type: str + required: no + default: present + choices: + - present + - absent +attributes: + check_mode: + support: full + diff_mode: + support: full +""" + +EXAMPLES = r""" +--- +- name: Create a Data Context + cloudera.cluster.data_context + host: example.cloudera.com + username: "jane_smith" + password: "S&peR4Ec*re" + name: "base_services" + cluster: "example_cluster" + services: ['hive','atlas','hdfs','ranger'] + state: present + +- name: Delete a data context + cloudera.cluster.data_context + host: example.cloudera.com + username: "jane_smith" + password: "S&peR4Ec*re" + name: "base_services" + state: absent + +- name: Update an existing data context + cloudera.cluster.data_context + host: example.cloudera.com + username: "jane_smith" + password: "S&peR4Ec*re" + name: "base_services" + cluster: "example_cluster" + services: ['hive','atlas','hdfs'] + state: present +""" + +RETURN = r""" +--- +data_context: + description: + - A dictionary containing details of data contexts within the cluster. + type: dict + elements: dict + returned: always + contains: + name: + description: + - The name of the data context. + type: str + returned: always + display_name: + description: + - The display name of the data context. + type: str + returned: always + nameservice: + description: + - The name service that data context belongs to. + type: str + returned: always + created_time: + description: + - The timestamp indicating when the data context was created. + type: str + returned: always + last_modified_time: + description: + - The timestamp indicating the last modification of the data context. + type: str + returned: always + services: + description: + - The list of services associated with data context. + type: list + returned: always + supported_service_types: + description: + - The list of supported services types within data context. + type: list + returned: always + allowed_cluster_versions: + description: + - The list of allowed cluster versions within data context. + type: list + returned: always + config_staleness_status: + description: + - Status of the configuration within data context. + type: str + returned: always + client_config_staleness_status: + description: + - Status of the client configuration within data context. + type: str + returned: always + health_summary: + description: + - The health status of the data context. + type: str + returned: always +""" + + +class ClouderaDataContext(ClouderaManagerMutableModule): + def __init__(self, module): + super(ClouderaDataContext, self).__init__(module) + + # Set the parameters + self.data_contex_name = self.get_param("name") + self.cluster_name = self.get_param("cluster") + self.services = self.get_param("services") + self.state = self.get_param("state") + # Initialize the return value + self.data_context_output = [] + self.changed = False + self.diff = {} + # Execute the logic + self.process() + + @ClouderaManagerMutableModule.handle_process + def process(self): + data_context_api = DataContextsResourceApi(self.api_client) + existing = [] + + try: + ClustersResourceApi(self.api_client).read_cluster(self.cluster_name) + except ApiException as ex: + if ex.status == 404: + self.module.fail_json( + msg="Cluster does not exist: " + self.cluster_name + ) + else: + raise ex + try: + existing = data_context_api.read_data_context( + data_context_name=self.data_contex_name + ).to_dict() + except ApiException as ex: + if ex.status == 500: + pass + else: + raise ex + + if self.state == "present": + if existing: + existing = {service["service_name"] for service in existing["services"]} + incoming = set(self.services) + + if self.module._diff: + self.diff.update( + before=list(existing - incoming), + after=list(incoming - existing), + ) + if existing != incoming: + services = [ + {"serviceName": service, "clusterName": self.cluster_name} + for service in incoming + ] + body = ApiDataContext(name=self.data_contex_name, services=services) + if not self.module.check_mode: + update_data_context = data_context_api.update_data_context( + body=body + ).to_dict() + + data_context_unparsed = ApiDataContextList( + items=[update_data_context] + ) + self.data_context_output = parse_data_context_result( + data_context_unparsed + ) + self.changed = True + else: + self.data_context_output = existing + else: + services = [ + {"serviceName": service, "clusterName": self.cluster_name} + for service in self.services + ] + body = ApiDataContext(name=self.data_contex_name, services=services) + if not self.module.check_mode: + create_data_context = data_context_api.create_data_context( + body=body + ).to_dict() + + data_context_unparsed = ApiDataContextList( + items=[create_data_context] + ) + self.data_context_output = parse_data_context_result( + data_context_unparsed + ) + self.changed = True + + if self.state == "absent": + if existing: + if not self.module.check_mode: + remove_data_context = data_context_api.delete_data_context( + data_context_name=self.data_contex_name + ).to_dict() + + data_context_unparsed = ApiDataContextList( + items=[remove_data_context] + ) + self.data_context_output = parse_data_context_result( + data_context_unparsed + ) + self.changed = True + + +def main(): + module = ClouderaManagerMutableModule.ansible_module( + argument_spec=dict( + name=dict(required=False, type="str"), + cluster=dict(required=True, type="str", aliases=["cluster_name"]), + services=dict(required=True, type="list"), + state=dict( + type="str", + default="present", + choices=["present", "absent"], + ), + ), + supports_check_mode=True, + ) + result = ClouderaDataContext(module) + + output = dict( + changed=False, + data_context=result.data_context_output, + ) + if module._diff: + output.update(diff=result.diff) + + 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/plugins/modules/data_context_info.py b/plugins/modules/data_context_info.py new file mode 100644 index 00000000..480fecfc --- /dev/null +++ b/plugins/modules/data_context_info.py @@ -0,0 +1,200 @@ +# 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 ( + ClouderaManagerMutableModule, +) +from cm_client import DataContextsResourceApi, ApiDataContextRef, ApiDataContextList + +from cm_client.rest import ApiException +from ansible_collections.cloudera.cluster.plugins.module_utils.data_context_utils import ( + parse_data_context_result, +) + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = r""" +--- +module: data_context_info +short_description: Retrieve details of data contexts +description: + - Retrieve details of a specific data context or all data contexts within the Cloudera Manager. +author: + - "Ronald Suplina (@rsuplina)" +requirements: + - cm_client +options: + name: + description: + - The name of the data context. + type: str + required: no +""" + +EXAMPLES = r""" +--- +- name: Gather details about specific data context + cloudera.cluster.data_context_info + host: example.cloudera.com + username: "jane_smith" + password: "S&peR4Ec*re" + name: "SDX" + +- name: Gather details about all data contexts within the cluster + cloudera.cluster.data_context_info + host: example.cloudera.com + username: "jane_smith" + password: "S&peR4Ec*re" +""" + +RETURN = r""" +--- +data_context_info: + description: + - List of data contexts within the cluster. + type: list + elements: dict + returned: always + contains: + name: + description: + - The name of the data context. + type: str + returned: always + display_name: + description: + - The display name of the data context. + type: str + returned: always + nameservice: + description: + - The name service that data context belongs to. + type: str + returned: always + created_time: + description: + - The timestamp indicating when the data context was created. + type: str + returned: always + last_modified_time: + description: + - The timestamp indicating the last modification of the data context. + type: str + returned: always + services: + description: + - The list of services associated with data context. + type: list + returned: always + supported_service_types: + description: + - The list of supported services types within data context. + type: list + returned: always + allowed_cluster_versions: + description: + - The list of allowed cluster versions within data context. + type: list + returned: always + config_staleness_status: + description: + - Status of the configuration within data context. + type: str + returned: always + client_config_staleness_status: + description: + - Status of the client configuration within data context. + type: str + returned: always + health_summary: + description: + - The health status of the data context. + type: str + returned: always +""" + + +class ClouderaDataContextInfo(ClouderaManagerMutableModule): + def __init__(self, module): + super(ClouderaDataContextInfo, self).__init__(module) + + # Set the parameters + self.data_contex_name = self.get_param("name") + + # Initialize the return value + self.data_context_info = [] + + # Execute the logic + self.process() + + @ClouderaManagerMutableModule.handle_process + def process(self): + data_context_api = DataContextsResourceApi(self.api_client) + try: + if self.data_contex_name: + data_contex = data_context_api.read_data_context( + data_context_name=self.data_contex_name + ).to_dict() + + data_context_unparsed = ApiDataContextList(items=[data_contex]) + self.data_context_info = parse_data_context_result( + data_context_unparsed + ) + + else: + data_contexts_info = data_context_api.read_data_contexts().to_dict() + + data_context_unparsed = ApiDataContextList( + items=data_contexts_info.get("items", []) + ) + self.data_context_info = parse_data_context_result( + data_context_unparsed + ) + + except ApiException as ex: + if ex.status == 500: + self.module.fail_json( + msg="Data Contex does not exist: " + self.data_contex_name + ) + else: + raise ex + + +def main(): + module = ClouderaManagerMutableModule.ansible_module( + argument_spec=dict( + name=dict(required=False, type="str"), + ), + supports_check_mode=False, + ) + result = ClouderaDataContextInfo(module) + + output = dict( + changed=False, + data_context_info=result.data_context_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/data_contex/test_data_contex.py b/tests/unit/plugins/modules/data_contex/test_data_contex.py new file mode 100644 index 00000000..fb1ae566 --- /dev/null +++ b/tests/unit/plugins/modules/data_contex/test_data_contex.py @@ -0,0 +1,98 @@ +# 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 logging +import os +import pytest + +from ansible_collections.cloudera.cluster.plugins.modules import data_context +from ansible_collections.cloudera.cluster.tests.unit import ( + AnsibleExitJson, + AnsibleFailJson, +) + +LOG = logging.getLogger(__name__) + + +@pytest.fixture +def conn(): + conn = dict(username=os.getenv("CM_USERNAME"), password=os.getenv("CM_PASSWORD")) + + if os.getenv("CM_HOST", None): + conn.update(host=os.getenv("CM_HOST")) + + if os.getenv("CM_PORT", None): + conn.update(port=os.getenv("CM_PORT")) + + # if os.getenv("CM_URL", None): + # conn.update(port=os.getenv("CM_URL")) + + # if os.getenv("CM_ENDPOINT", None): + # conn.update(url=os.getenv("CM_ENDPOINT")) + + if os.getenv("CM_PROXY", None): + conn.update(proxy=os.getenv("CM_PROXY")) + + return { + **conn, + "verify_tls": "no", + "debug": "no", + } + + +def test_create_data_context(module_args, conn): + conn.update( + name="Test_DataContext", + cluster="example_cluster", + services=["hive", "atlas", "hdfs"], + state="present", + ) + + module_args(conn) + + with pytest.raises(AnsibleExitJson) as e: + data_context.main() + + LOG.info(str(e.value.data_context)) + + +def test_update_data_context(module_args, conn): + conn.update( + name="Test_DataContext", + cluster="example_cluster", + services=["atlas", "hdfs"], + state="present", + ) + + module_args(conn) + + with pytest.raises(AnsibleExitJson) as e: + data_context.main() + + LOG.info(str(e.value.data_context)) + + +def test_remove_data_context(module_args, conn): + conn.update(name="Test_DataContext", state="absent") + + module_args(conn) + + with pytest.raises(AnsibleExitJson) as e: + data_context.main() + + LOG.info(str(e.value.data_context)) diff --git a/tests/unit/plugins/modules/data_contex_info/test_data_context_info.py b/tests/unit/plugins/modules/data_contex_info/test_data_context_info.py new file mode 100644 index 00000000..bd1be811 --- /dev/null +++ b/tests/unit/plugins/modules/data_contex_info/test_data_context_info.py @@ -0,0 +1,82 @@ +# 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 logging +import os +import pytest + +from ansible_collections.cloudera.cluster.plugins.modules import data_context_info +from ansible_collections.cloudera.cluster.tests.unit import ( + AnsibleExitJson, + AnsibleFailJson, +) + +LOG = logging.getLogger(__name__) + + +@pytest.fixture +def conn(): + conn = dict(username=os.getenv("CM_USERNAME"), password=os.getenv("CM_PASSWORD")) + + if os.getenv("CM_HOST", None): + conn.update(host=os.getenv("CM_HOST")) + + if os.getenv("CM_PORT", None): + conn.update(port=os.getenv("CM_PORT")) + + # if os.getenv("CM_URL", None): + # conn.update(port=os.getenv("CM_URL")) + + # if os.getenv("CM_ENDPOINT", None): + # conn.update(url=os.getenv("CM_ENDPOINT")) + + if os.getenv("CM_PROXY", None): + conn.update(proxy=os.getenv("CM_PROXY")) + + return { + **conn, + "verify_tls": "no", + "debug": "no", + } + + +def test_data_context_info(module_args, conn): + conn.update( + host=os.getenv("CM_HOST"), + name="Test_DataContext", + ) + + module_args(conn) + + with pytest.raises(AnsibleExitJson) as e: + data_context_info.main() + + LOG.info(str(e.value.data_context_info)) + + +def test_data_context_info_all_hosts(module_args, conn): + conn.update( + host=os.getenv("CM_HOST"), + ) + + module_args(conn) + + with pytest.raises(AnsibleExitJson) as e: + data_context_info.main() + + LOG.info(str(e.value.data_context_info)) From ad58700b1d91fb5571d45a085d997b599f89f5ec Mon Sep 17 00:00:00 2001 From: rsuplina Date: Fri, 19 Jul 2024 11:29:20 +0100 Subject: [PATCH 2/5] Add Data Context Utils Signed-off-by: rsuplina --- plugins/module_utils/data_context_utils.py | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 plugins/module_utils/data_context_utils.py diff --git a/plugins/module_utils/data_context_utils.py b/plugins/module_utils/data_context_utils.py new file mode 100644 index 00000000..1f4bc2dc --- /dev/null +++ b/plugins/module_utils/data_context_utils.py @@ -0,0 +1,50 @@ +# Copyright 2024 Cloudera, Inc. +# +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distribuFd 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. + +""" +A common functions for Cloudera Manager service management +""" + +from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import ( + _parse_output, +) +from cm_client import ApiDataContextList + + +DATA_CONTEXT_OUTPUT = [ + "name", + "display_name", + "nameservice", + "created_time", + "last_modified_time", + "services", + # "services_details", + "supported_service_types", + "allowed_cluster_versions", + "config_staleness_status", + "client_config_staleness_status", + "health_summary", +] + + +def _parse_output(data: dict, keys: list) -> dict: + return {key: data[key] for key in keys if key in data} + + +def _remove_unwanted_fields(data: dict) -> dict: + return _parse_output(data, DATA_CONTEXT_OUTPUT) + + +def parse_data_context_result(data_contexts: ApiDataContextList) -> list: + return [_remove_unwanted_fields(item) for item in data_contexts.items] From 9694b4036ba90d3bc3f4a69f97d3b29ff2c7fa83 Mon Sep 17 00:00:00 2001 From: rsuplina Date: Fri, 19 Jul 2024 15:35:15 +0100 Subject: [PATCH 3/5] Add required updates Signed-off-by: rsuplina --- plugins/modules/data_context.py | 64 ++++++++++++---------------- plugins/modules/data_context_info.py | 43 ++++++++----------- 2 files changed, 46 insertions(+), 61 deletions(-) diff --git a/plugins/modules/data_context.py b/plugins/modules/data_context.py index fe6278e4..5ee9e092 100644 --- a/plugins/modules/data_context.py +++ b/plugins/modules/data_context.py @@ -15,7 +15,7 @@ from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import ( ClouderaManagerMutableModule, ) -from cm_client import DataContextsResourceApi, ApiDataContextRef, ApiDataContextList +from cm_client import DataContextsResourceApi, ApiDataContextList from cm_client import ( ClustersResourceApi, @@ -65,7 +65,7 @@ state: description: - If I(state=present), the data context will be created or updated. - - If I(state=absent), the data context will be deleted deleted. + - If I(state=absent), the data context will be deleted type: str required: no default: present @@ -219,30 +219,28 @@ def process(self): if self.state == "present": if existing: - existing = {service["service_name"] for service in existing["services"]} - incoming = set(self.services) - - if self.module._diff: - self.diff.update( - before=list(existing - incoming), - after=list(incoming - existing), - ) - if existing != incoming: + existing_service = { + service["service_name"] for service in existing["services"] + } + incoming_service = set(self.services) + if existing_service != incoming_service: + if self.module._diff: + self.diff.update( + before=list(existing_service - incoming_service), + after=list(incoming_service - existing_service), + ) services = [ {"serviceName": service, "clusterName": self.cluster_name} - for service in incoming + for service in incoming_service ] - body = ApiDataContext(name=self.data_contex_name, services=services) if not self.module.check_mode: update_data_context = data_context_api.update_data_context( - body=body + body=ApiDataContext( + name=self.data_contex_name, services=services + ) ).to_dict() - - data_context_unparsed = ApiDataContextList( - items=[update_data_context] - ) self.data_context_output = parse_data_context_result( - data_context_unparsed + ApiDataContextList(items=[update_data_context]) ) self.changed = True else: @@ -252,42 +250,33 @@ def process(self): {"serviceName": service, "clusterName": self.cluster_name} for service in self.services ] - body = ApiDataContext(name=self.data_contex_name, services=services) if not self.module.check_mode: create_data_context = data_context_api.create_data_context( - body=body + body=ApiDataContext( + name=self.data_contex_name, services=services + ) ).to_dict() - data_context_unparsed = ApiDataContextList( - items=[create_data_context] - ) self.data_context_output = parse_data_context_result( - data_context_unparsed + ApiDataContextList(items=[create_data_context]) ) self.changed = True if self.state == "absent": if existing: if not self.module.check_mode: - remove_data_context = data_context_api.delete_data_context( + data_context_api.delete_data_context( data_context_name=self.data_contex_name ).to_dict() - - data_context_unparsed = ApiDataContextList( - items=[remove_data_context] - ) - self.data_context_output = parse_data_context_result( - data_context_unparsed - ) self.changed = True def main(): module = ClouderaManagerMutableModule.ansible_module( argument_spec=dict( - name=dict(required=False, type="str"), - cluster=dict(required=True, type="str", aliases=["cluster_name"]), - services=dict(required=True, type="list"), + name=dict(required=True, type="str"), + cluster=dict(required=False, type="str", aliases=["cluster_name"]), + services=dict(required=False, type="list"), state=dict( type="str", default="present", @@ -295,6 +284,9 @@ def main(): ), ), supports_check_mode=True, + required_if=[ + ("state", "present", ("cluster", "services"), False), + ], ) result = ClouderaDataContext(module) diff --git a/plugins/modules/data_context_info.py b/plugins/modules/data_context_info.py index 480fecfc..0af1c39e 100644 --- a/plugins/modules/data_context_info.py +++ b/plugins/modules/data_context_info.py @@ -134,7 +134,7 @@ def __init__(self, module): super(ClouderaDataContextInfo, self).__init__(module) # Set the parameters - self.data_contex_name = self.get_param("name") + self.data_context_name = self.get_param("name") # Initialize the return value self.data_context_info = [] @@ -145,34 +145,27 @@ def __init__(self, module): @ClouderaManagerMutableModule.handle_process def process(self): data_context_api = DataContextsResourceApi(self.api_client) - try: - if self.data_contex_name: + if self.data_context_name: + try: data_contex = data_context_api.read_data_context( - data_context_name=self.data_contex_name + data_context_name=self.data_context_name ).to_dict() - - data_context_unparsed = ApiDataContextList(items=[data_contex]) - self.data_context_info = parse_data_context_result( - data_context_unparsed - ) - - else: - data_contexts_info = data_context_api.read_data_contexts().to_dict() - - data_context_unparsed = ApiDataContextList( - items=data_contexts_info.get("items", []) - ) self.data_context_info = parse_data_context_result( - data_context_unparsed - ) - - except ApiException as ex: - if ex.status == 500: - self.module.fail_json( - msg="Data Contex does not exist: " + self.data_contex_name + ApiDataContextList(items=[data_contex]) ) - else: - raise ex + except ApiException as ex: + if ex.status == 500: + self.module.fail_json( + msg="Data Context does not exist: " + self.data_context_name + ) + else: + raise ex + else: + data_contexts_info = data_context_api.read_data_contexts().to_dict() + + self.data_context_info = parse_data_context_result( + ApiDataContextList(items=data_contexts_info.get("items", [])) + ) def main(): From 9a2f12668c034c435befeed1cc958c64e556eebf Mon Sep 17 00:00:00 2001 From: rsuplina Date: Fri, 19 Jul 2024 15:52:11 +0100 Subject: [PATCH 4/5] Add min cm-client version, edit doc. typo Signed-off-by: rsuplina --- plugins/modules/data_context.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/data_context.py b/plugins/modules/data_context.py index 5ee9e092..a01d752e 100644 --- a/plugins/modules/data_context.py +++ b/plugins/modules/data_context.py @@ -45,7 +45,7 @@ author: - "Ronald Suplina (@rsuplina)" requirements: - - cm_client + - cm-client >= 54 options: name: description: @@ -116,7 +116,7 @@ description: - A dictionary containing details of data contexts within the cluster. type: dict - elements: dict + elements: complex returned: always contains: name: From 81e4bc14e666588a9fe9b7938baebcefe8d90c48 Mon Sep 17 00:00:00 2001 From: rsuplina Date: Mon, 29 Jul 2024 16:47:59 +0100 Subject: [PATCH 5/5] Add Requested changes Signed-off-by: rsuplina --- plugins/module_utils/data_context_utils.py | 6 +----- plugins/modules/data_context.py | 15 +++++++++++++-- plugins/modules/data_context_info.py | 15 +++++++++------ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/plugins/module_utils/data_context_utils.py b/plugins/module_utils/data_context_utils.py index 1f4bc2dc..4b3f54f7 100644 --- a/plugins/module_utils/data_context_utils.py +++ b/plugins/module_utils/data_context_utils.py @@ -42,9 +42,5 @@ def _parse_output(data: dict, keys: list) -> dict: return {key: data[key] for key in keys if key in data} -def _remove_unwanted_fields(data: dict) -> dict: - return _parse_output(data, DATA_CONTEXT_OUTPUT) - - def parse_data_context_result(data_contexts: ApiDataContextList) -> list: - return [_remove_unwanted_fields(item) for item in data_contexts.items] + return [_parse_output(item, DATA_CONTEXT_OUTPUT) for item in data_contexts.items] diff --git a/plugins/modules/data_context.py b/plugins/modules/data_context.py index a01d752e..8beab1ee 100644 --- a/plugins/modules/data_context.py +++ b/plugins/modules/data_context.py @@ -52,11 +52,16 @@ - The name of the data context. type: str required: yes + aliases: + - context_name + - data_context_name cluster: description: - The name of the Cloudera Manager cluster. type: str required: no + aliases: + - cluster_name services: description: - A list of services that the data context will include. @@ -186,10 +191,12 @@ def __init__(self, module): self.cluster_name = self.get_param("cluster") self.services = self.get_param("services") self.state = self.get_param("state") + # Initialize the return value self.data_context_output = [] self.changed = False self.diff = {} + # Execute the logic self.process() @@ -212,7 +219,9 @@ def process(self): data_context_name=self.data_contex_name ).to_dict() except ApiException as ex: - if ex.status == 500: + if ( + ex.status == 500 + ): # Future change: Expected server response code will be 404 pass else: raise ex @@ -274,7 +283,9 @@ def process(self): def main(): module = ClouderaManagerMutableModule.ansible_module( argument_spec=dict( - name=dict(required=True, type="str"), + name=dict( + required=True, type="str", aliases=["context_name", "data_context_name"] + ), cluster=dict(required=False, type="str", aliases=["cluster_name"]), services=dict(required=False, type="list"), state=dict( diff --git a/plugins/modules/data_context_info.py b/plugins/modules/data_context_info.py index 0af1c39e..22268e3f 100644 --- a/plugins/modules/data_context_info.py +++ b/plugins/modules/data_context_info.py @@ -44,6 +44,9 @@ - The name of the data context. type: str required: no + aliases: + - context_name + - data_context_name """ EXAMPLES = r""" @@ -154,11 +157,7 @@ def process(self): ApiDataContextList(items=[data_contex]) ) except ApiException as ex: - if ex.status == 500: - self.module.fail_json( - msg="Data Context does not exist: " + self.data_context_name - ) - else: + if ex.status != 500: raise ex else: data_contexts_info = data_context_api.read_data_contexts().to_dict() @@ -171,7 +170,11 @@ def process(self): def main(): module = ClouderaManagerMutableModule.ansible_module( argument_spec=dict( - name=dict(required=False, type="str"), + name=dict( + required=False, + type="str", + aliases=["context_name", "data_context_name"], + ), ), supports_check_mode=False, )