From d730d1739739335b768c377d4f699b4eb1e29511 Mon Sep 17 00:00:00 2001 From: Ujjwal Nasra <125353741+unasra@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:11:23 +0530 Subject: [PATCH 1/7] infra token --- meta/runtime.yml | 5 + plugins/modules/infra_join_token.py | 281 ++++++++++++++++++ plugins/modules/infra_join_token_info.py | 231 ++++++++++++++ .../targets/infra_join_token/tasks/main.yml | 147 +++++++++ .../infra_join_token_info/tasks/main.yml | 54 ++++ 5 files changed, 718 insertions(+) create mode 100644 plugins/modules/infra_join_token.py create mode 100644 plugins/modules/infra_join_token_info.py create mode 100644 tests/integration/targets/infra_join_token/tasks/main.yml create mode 100644 tests/integration/targets/infra_join_token_info/tasks/main.yml diff --git a/meta/runtime.yml b/meta/runtime.yml index 55f479e..7b7a0cf 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -4,6 +4,7 @@ action_groups: extend_group: - ipam - dns + - infra dns: - dns_view - dns_view_info @@ -19,6 +20,10 @@ action_groups: - ipam_address_block - ipam_address_block_info + infra: + - infra_join_token + - infra_join_token_info + plugin_routing: modules: b1_ipam_ip_space: diff --git a/plugins/modules/infra_join_token.py b/plugins/modules/infra_join_token.py new file mode 100644 index 0000000..ef6df32 --- /dev/null +++ b/plugins/modules/infra_join_token.py @@ -0,0 +1,281 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: Infoblox Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = r""" +--- +module: infra_join_token +short_description: Manage UIJoinToken +description: + - Manage UIJoinToken +version_added: 2.0.0 +author: Infoblox Inc. (@infobloxopen) +options: + id: + description: + - ID of the object + type: str + required: false + state: + description: + - Indicate desired state of the object + type: str + required: false + choices: + - present + - absent + default: present + description: + description: "" + type: str + expires_at: + description: "" + type: str + name: + description: "" + type: str + tags: + description: "" + type: dict + +extends_documentation_fragment: + - infoblox.bloxone.common +""" # noqa: E501 + +EXAMPLES = r""" + - name: Create a UI Join token + infoblox.bloxone.infra_join_token: + name: "example_token" + state: "present" + + - name: Create a UI Join Token with Additional Fields + infoblox.bloxone.infra_join_token: + name: "example_token" + description: "Example Join Token" + tags: + location: "my-location" + + - name: Delete a UI Join token + infoblox.bloxone.infra_join_token: + name: "example_token" + state: "absent" +""" + +RETURN = r""" +id: + description: + - ID of the UIJoinToken object + type: str + returned: Always +item: + description: + - UIJoinToken object + type: complex + returned: Always + contains: + deleted_at: + description: "" + type: str + returned: Always + description: + description: "" + type: str + returned: Always + expires_at: + description: "" + type: str + returned: Always + id: + description: + - "The resource identifier." + type: str + returned: Always + last_used_at: + description: "" + type: str + returned: Always + name: + description: "" + type: str + returned: Always + status: + description: + - "derived field, \"active\" when expires_at and deleted_at are null." + type: dict + returned: Always + contains: + tags: + description: "" + type: dict + returned: Always + token_id: + description: + - "first half of the token." + type: str + returned: Always + use_counter: + description: "" + type: int + returned: Always +""" # noqa: E501 + +from ansible_collections.infoblox.bloxone.plugins.module_utils.modules import BloxoneAnsibleModule + +try: + from bloxone_client import ApiException, NotFoundException + from infra_provision import JoinToken, UIJoinTokenApi +except ImportError: + pass # Handled by BloxoneAnsibleModule + + +class UIJoinTokenModule(BloxoneAnsibleModule): + def __init__(self, *args, **kwargs): + super(UIJoinTokenModule, self).__init__(*args, **kwargs) + + exclude = ["state", "csp_url", "api_key", "id"] + self._payload_params = {k: v for k, v in self.params.items() if v is not None and k not in exclude} + self._payload = JoinToken.from_dict(self._payload_params) + self._existing = None + + @property + def existing(self): + return self._existing + + @existing.setter + def existing(self, value): + self._existing = value + + @property + def payload_params(self): + return self._payload_params + + @property + def payload(self): + return self._payload + + def payload_changed(self): + if self.existing is None: + # if existing is None, then it is a create operation + return True + + return self.is_changed(self.existing.model_dump(by_alias=True, exclude_none=True), self.payload_params) + + def find(self): + if self.params["id"] is not None: + try: + resp = UIJoinTokenApi(self.client).read(self.params["id"]) + return resp.result + except NotFoundException as e: + if self.params["state"] == "absent": + return None + raise e + else: + filter = f"name=='{self.params['name']}'" + resp = UIJoinTokenApi(self.client).list(filter=filter) + + # If no results, set results to empty list + if not resp.results: + resp.results = [] + + # If result is REVOKED, remove it from the list + for i in resp.results: + if i.status == "REVOKED": + resp.results.pop(resp.results.index(i)) + + if len(resp.results) == 1: + return resp.results[0] + if len(resp.results) > 1: + self.fail_json(msg=f"Found multiple View: {resp.results}") + if len(resp.results) == 0: + return None + + def create(self): + if self.check_mode: + return None + + resp = UIJoinTokenApi(self.client).create(body=self.payload) + return resp.result.model_dump(by_alias=True, exclude_none=True) + + def update(self): + if self.check_mode: + return None + + update_body = self.payload + update_body = self.validate_readonly_on_update(self.existing, update_body, ["name", "description"]) + + resp = UIJoinTokenApi(self.client).update(id=self.existing.id, body=update_body) + return resp.result.model_dump(by_alias=True, exclude_none=True) + + def delete(self): + if self.check_mode: + return + + UIJoinTokenApi(self.client).delete(self.existing.id) + + def run_command(self): + result = dict(changed=False, object={}, id=None) + + # based on the state that is passed in, we will execute the appropriate + # functions + try: + self.existing = self.find() + item = {} + if self.params["state"] == "present" and self.existing is None: + item = self.create() + result["changed"] = True + result["msg"] = "UIJoinToken created" + elif self.params["state"] == "present" and self.existing is not None: + if self.payload_changed(): + item = self.update() + result["changed"] = True + result["msg"] = "UIJoinToken updated" + elif self.params["state"] == "absent" and self.existing is not None: + self.delete() + result["changed"] = True + result["msg"] = "UIJoinToken deleted" + + if self.check_mode: + # if in check mode, do not update the result or the diff, just return the changed state + self.exit_json(**result) + + result["diff"] = dict( + before=self.existing.model_dump(by_alias=True, exclude_none=True) if self.existing is not None else {}, + after=item, + ) + result["object"] = item + result["id"] = ( + self.existing.id if self.existing is not None else item["id"] if (item and "id" in item) else None + ) + except ApiException as e: + self.fail_json(msg=f"Failed to execute command: {e.status} {e.reason} {e.body}") + + self.exit_json(**result) + + +def main(): + module_args = dict( + id=dict(type="str", required=False), + state=dict(type="str", required=False, choices=["present", "absent"], default="present"), + description=dict(type="str"), + expires_at=dict(type="str"), + name=dict(type="str"), + tags=dict(type="dict"), + ) + + module = UIJoinTokenModule( + argument_spec=module_args, + supports_check_mode=True, + required_if=[("state", "present", ["name"])], + ) + + module.run_command() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/infra_join_token_info.py b/plugins/modules/infra_join_token_info.py new file mode 100644 index 0000000..ff19415 --- /dev/null +++ b/plugins/modules/infra_join_token_info.py @@ -0,0 +1,231 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: Infoblox Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: infra_join_token_info +short_description: Manage UIJoinToken +description: + - Manage UIJoinToken +version_added: 2.0.0 +author: Infoblox Inc. (@infobloxopen) +options: + id: + description: + - ID of the object + type: str + required: false + filters: + description: + - Filter dict to filter objects + type: dict + required: false + filter_query: + description: + - Filter query to filter objects + type: str + required: false + tag_filters: + description: + - Filter dict to filter objects by tags + type: dict + required: false + tag_filter_query: + description: + - Filter query to filter objects by tags + type: str + required: false + +extends_documentation_fragment: + - infoblox.bloxone.common +""" # noqa: E501 + +EXAMPLES = r""" + - name: Get UI Join Token information by ID + infoblox.bloxone.infra_join_token_info: + id: "{{ join_token_id }}" + + - name: Get a UI Join Token information by filters (e.g. name) + infoblox.bloxone.infra_join_token_info: + filters: + name: "example_token" + + - name: Get a UI Join Token information by raw filter query + infoblox.bloxone.infra_join_token_info: + filter_query: "name=='example_token'" + + - name: Get a UI Join Token information by tag filters + infoblox.bloxone.infra_join_token_info: + tag_filters: + location: "site-1" +""" # noqa: E501 + +RETURN = r""" +id: + description: + - ID of the UIJoinToken object + type: str + returned: Always +objects: + description: + - UIJoinToken object + type: list + elements: dict + returned: Always + contains: + deleted_at: + description: "" + type: str + returned: Always + description: + description: "" + type: str + returned: Always + expires_at: + description: "" + type: str + returned: Always + id: + description: + - "The resource identifier." + type: str + returned: Always + last_used_at: + description: "" + type: str + returned: Always + name: + description: "" + type: str + returned: Always + status: + description: + - "derived field, \"active\" when expires_at and deleted_at are null." + type: dict + returned: Always + contains: + tags: + description: "" + type: dict + returned: Always + token_id: + description: + - "first half of the token." + type: str + returned: Always + use_counter: + description: "" + type: int + returned: Always +""" # noqa: E501 + +from ansible_collections.infoblox.bloxone.plugins.module_utils.modules import BloxoneAnsibleModule + +try: + from bloxone_client import ApiException, NotFoundException + from infra_provision import UIJoinTokenApi +except ImportError: + pass # Handled by BloxoneAnsibleModule + + +class UIJoinTokenInfoModule(BloxoneAnsibleModule): + def __init__(self, *args, **kwargs): + super(UIJoinTokenInfoModule, self).__init__(*args, **kwargs) + self._existing = None + self._limit = 1000 + + def find_by_id(self): + try: + resp = UIJoinTokenApi(self.client).read(self.params["id"]) + return [resp.result] + except NotFoundException as e: + return None + + def find(self): + if self.params["id"] is not None: + return self.find_by_id() + + filter_str = None + if self.params["filters"] is not None: + filter_str = " and ".join([f"{k}=='{v}'" for k, v in self.params["filters"].items()]) + elif self.params["filter_query"] is not None: + filter_str = self.params["filter_query"] + + tag_filter_str = None + if self.params["tag_filters"] is not None: + tag_filter_str = " and ".join([f"{k}=='{v}'" for k, v in self.params["tag_filters"].items()]) + elif self.params["tag_filter_query"] is not None: + tag_filter_str = self.params["tag_filter_query"] + + all_results = [] + offset = 0 + + while True: + try: + resp = UIJoinTokenApi(self.client).list( + offset=offset, limit=self._limit, filter=filter_str, tfilter=tag_filter_str + ) + if not resp.results: + resp.results = [] + + # Removes REVOKED tokens from the list + for i in resp.results: + if i.status == "REVOKED": + resp.results.pop(resp.results.index(i)) + + all_results.extend(resp.results) + + if len(resp.results) < self._limit: + break + offset += self._limit + + except ApiException as e: + self.fail_json(msg=f"Failed to execute command: {e.status} {e.reason} {e.body}") + + return all_results + + def run_command(self): + result = dict(objects=[]) + + if self.check_mode: + self.exit_json(**result) + + find_results = self.find() + + all_results = [] + for r in find_results: + all_results.append(r.model_dump(by_alias=True, exclude_none=True)) + + result["objects"] = all_results + self.exit_json(**result) + + +def main(): + # define available arguments/parameters a user can pass to the module + module_args = dict( + id=dict(type="str", required=False), + filters=dict(type="dict", required=False), + filter_query=dict(type="str", required=False), + tag_filters=dict(type="dict", required=False), + tag_filter_query=dict(type="str", required=False), + ) + + module = UIJoinTokenInfoModule( + argument_spec=module_args, + supports_check_mode=True, + mutually_exclusive=[ + ["id", "filters", "filter_query"], + ["id", "tag_filters", "tag_filter_query"], + ], + ) + module.run_command() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/infra_join_token/tasks/main.yml b/tests/integration/targets/infra_join_token/tasks/main.yml new file mode 100644 index 0000000..510b850 --- /dev/null +++ b/tests/integration/targets/infra_join_token/tasks/main.yml @@ -0,0 +1,147 @@ +--- +- module_defaults: + group/infoblox.bloxone.all: + csp_url: "{{ csp_url }}" + api_key: "{{ api_key }}" + + block: + # Create a random name to avoid conflicts + - ansible.builtin.set_fact: + name: "test-ui-join-token-{{ 999999 | random | string }}" + name2: "test-ui-join-token-{{ 999999 | random | string }}" + tags: test + + - name: Create an UI Join Token (check mode) + infoblox.bloxone.infra_join_token: + name: "{{ name }}" + state: present + check_mode: true + register: ui_join_token + - name: Get Information about the UI Join Token + infoblox.bloxone.infra_join_token_info: + filters: + name: "{{ name }}" + register: ui_join_token_info + - assert: + that: + - ui_join_token is changed + - ui_join_token_info is not failed + - ui_join_token_info.objects | length == 0 + + - name: Create an UI Join Token + infoblox.bloxone.infra_join_token: + name: "{{ name }}" + state: present + register: ui_join_token + tags: test + - name: Get Information about the UI Join Token + tags: test + infoblox.bloxone.infra_join_token_info: + filters: + name: "{{ name }}" + register: ui_join_token_info + - assert: + that: + - ui_join_token is changed + - ui_join_token_info is not failed + - ui_join_token_info.objects | length == 1 + tags: test + + - name: Create an UI Join Token (idempotent) + infoblox.bloxone.infra_join_token: + name: "{{ name }}" + state: present + register: ui_join_token + - assert: + that: + - ui_join_token is not changed + - ui_join_token is not failed + + - name: Delete the UI Join Token (check mode) + infoblox.bloxone.infra_join_token: + name: "{{ name }}" + state: absent + check_mode: true + register: ui_join_token + - name: Get Information about the UI Join Token + infoblox.bloxone.infra_join_token_info: + filters: + name: "{{ name }}" + register: ui_join_token_info + - assert: + that: + - ui_join_token is changed + - ui_join_token_info is not failed + - ui_join_token_info.objects | length == 1 + + - name: Delete the UI Join Token + infoblox.bloxone.infra_join_token: + name: "{{ name }}" + state: absent + register: ui_join_token + tags: test + - name: Get Information about the UI Join Token + tags: test + infoblox.bloxone.infra_join_token_info: + filters: + name: "{{ name }}" + register: ui_join_token_info + - assert: + that: + - ui_join_token is changed + - ui_join_token_info is not failed + - ui_join_token_info.objects | length == 0 + tags: test + + - name: Delete the UI Join Token (idempotent) + infoblox.bloxone.infra_join_token: + name: "{{ name }}" + state: absent + register: ui_join_token + - assert: + that: + - ui_join_token is not changed + - ui_join_token is not failed + + - name: Create an UI Join Token with Description + infoblox.bloxone.infra_join_token: + name: "{{ name2 }}" + description: "Test UI Join Token" + state: present + register: ui_join_token + - name: Get Information about the UI Join Token + infoblox.bloxone.infra_join_token_info: + filters: + name: "{{ name2 }}" + register: ui_join_token_info + - assert: + that: + - ui_join_token is changed + - ui_join_token_info is not failed + - ui_join_token_info.objects | length == 1 + + - name: Create an UI Join Token with Tags + infoblox.bloxone.infra_join_token: + name: "{{ name2 }}" + description: "Test UI Join Token" + tags: + location: "site-1" + state: present + register: ui_join_token + - name: Get Information about the UI Join Token + infoblox.bloxone.infra_join_token_info: + filters: + name: "{{ name2 }}" + register: ui_join_token_info + - assert: + that: + - ui_join_token is changed + - ui_join_token_info is not failed + - ui_join_token_info.objects | length == 1 + + always: + - name: "Clean up the Join Token" + infoblox.bloxone.infra_join_token: + name: "{{ name2 }}" + state: absent + ignore_errors: true \ No newline at end of file diff --git a/tests/integration/targets/infra_join_token_info/tasks/main.yml b/tests/integration/targets/infra_join_token_info/tasks/main.yml new file mode 100644 index 0000000..cb97359 --- /dev/null +++ b/tests/integration/targets/infra_join_token_info/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- module_defaults: + group/infoblox.bloxone.all: + csp_url: "{{ csp_url }}" + api_key: "{{ api_key }}" + + block: + - ansible.builtin.set_fact: + name: "test-ui-join-token-{{ 999999 | random | string }}" + tag_value: "site-{{ 999999 | random | string }}" + + - name: Create an UI Join Token + infoblox.bloxone.infra_join_token: + name: "{{ name }}" + tags: + location: "{{ tag_value }}" + state: present + register: ui_join_token + + - name: Get Information about the UI Join Token by filters (Name) + infoblox.bloxone.infra_join_token_info: + filters: + name: "{{ name }}" + register: ui_join_token_info + - assert: + that: + - ui_join_token_info.objects | length == 1 + - ui_join_token_info.objects[0].id == ui_join_token.id + + - name: Get Information about the UI Join Token by filter query + infoblox.bloxone.infra_join_token_info: + filter_query: "name=='{{ name }}'" + register: ui_join_token_info + - assert: + that: + - ui_join_token_info.objects | length == 1 + - ui_join_token_info.objects[0].id == ui_join_token.id + + - name: Get Information about the UI Join Token by tag filter + infoblox.bloxone.infra_join_token_info: + tag_filters: + location: "{{ tag_value }}" + register: ui_join_token_info + - assert: + that: + - ui_join_token_info.objects | length == 1 + - ui_join_token_info.objects[0].id == ui_join_token.id + + always: + - name: "Clean up the Join Token" + infoblox.bloxone.infra_join_token: + name: "{{ name }}" + state: absent + ignore_errors: true \ No newline at end of file From 51f7afff0f6bf8ec9ebb82152dcbb50e20efb5fd Mon Sep 17 00:00:00 2001 From: Ujjwal Nasra <125353741+unasra@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:21:33 +0530 Subject: [PATCH 2/7] Removed RETURN from documentation --- plugins/modules/infra_join_token.py | 58 ----------------------- plugins/modules/infra_join_token_info.py | 59 ------------------------ 2 files changed, 117 deletions(-) diff --git a/plugins/modules/infra_join_token.py b/plugins/modules/infra_join_token.py index ef6df32..c862027 100644 --- a/plugins/modules/infra_join_token.py +++ b/plugins/modules/infra_join_token.py @@ -67,64 +67,6 @@ state: "absent" """ -RETURN = r""" -id: - description: - - ID of the UIJoinToken object - type: str - returned: Always -item: - description: - - UIJoinToken object - type: complex - returned: Always - contains: - deleted_at: - description: "" - type: str - returned: Always - description: - description: "" - type: str - returned: Always - expires_at: - description: "" - type: str - returned: Always - id: - description: - - "The resource identifier." - type: str - returned: Always - last_used_at: - description: "" - type: str - returned: Always - name: - description: "" - type: str - returned: Always - status: - description: - - "derived field, \"active\" when expires_at and deleted_at are null." - type: dict - returned: Always - contains: - tags: - description: "" - type: dict - returned: Always - token_id: - description: - - "first half of the token." - type: str - returned: Always - use_counter: - description: "" - type: int - returned: Always -""" # noqa: E501 - from ansible_collections.infoblox.bloxone.plugins.module_utils.modules import BloxoneAnsibleModule try: diff --git a/plugins/modules/infra_join_token_info.py b/plugins/modules/infra_join_token_info.py index ff19415..3c633a4 100644 --- a/plugins/modules/infra_join_token_info.py +++ b/plugins/modules/infra_join_token_info.py @@ -66,65 +66,6 @@ location: "site-1" """ # noqa: E501 -RETURN = r""" -id: - description: - - ID of the UIJoinToken object - type: str - returned: Always -objects: - description: - - UIJoinToken object - type: list - elements: dict - returned: Always - contains: - deleted_at: - description: "" - type: str - returned: Always - description: - description: "" - type: str - returned: Always - expires_at: - description: "" - type: str - returned: Always - id: - description: - - "The resource identifier." - type: str - returned: Always - last_used_at: - description: "" - type: str - returned: Always - name: - description: "" - type: str - returned: Always - status: - description: - - "derived field, \"active\" when expires_at and deleted_at are null." - type: dict - returned: Always - contains: - tags: - description: "" - type: dict - returned: Always - token_id: - description: - - "first half of the token." - type: str - returned: Always - use_counter: - description: "" - type: int - returned: Always -""" # noqa: E501 - from ansible_collections.infoblox.bloxone.plugins.module_utils.modules import BloxoneAnsibleModule try: From 398fd32ec847e3da1d72f9d0f5f65fb3f9108bab Mon Sep 17 00:00:00 2001 From: Ujjwal Nasra <125353741+unasra@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:05:37 +0530 Subject: [PATCH 3/7] misc changes --- .../targets/infra_join_token/tasks/main.yml | 20 +++++++------------ .../infra_join_token_info/tasks/main.yml | 6 +++--- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/tests/integration/targets/infra_join_token/tasks/main.yml b/tests/integration/targets/infra_join_token/tasks/main.yml index 510b850..126c719 100644 --- a/tests/integration/targets/infra_join_token/tasks/main.yml +++ b/tests/integration/targets/infra_join_token/tasks/main.yml @@ -9,9 +9,8 @@ - ansible.builtin.set_fact: name: "test-ui-join-token-{{ 999999 | random | string }}" name2: "test-ui-join-token-{{ 999999 | random | string }}" - tags: test - - name: Create an UI Join Token (check mode) + - name: Create a UI Join Token (check mode) infoblox.bloxone.infra_join_token: name: "{{ name }}" state: present @@ -28,14 +27,12 @@ - ui_join_token_info is not failed - ui_join_token_info.objects | length == 0 - - name: Create an UI Join Token + - name: Create a UI Join Token infoblox.bloxone.infra_join_token: name: "{{ name }}" state: present register: ui_join_token - tags: test - name: Get Information about the UI Join Token - tags: test infoblox.bloxone.infra_join_token_info: filters: name: "{{ name }}" @@ -45,9 +42,8 @@ - ui_join_token is changed - ui_join_token_info is not failed - ui_join_token_info.objects | length == 1 - tags: test - - name: Create an UI Join Token (idempotent) + - name: Create a UI Join Token (idempotent) infoblox.bloxone.infra_join_token: name: "{{ name }}" state: present @@ -79,9 +75,7 @@ name: "{{ name }}" state: absent register: ui_join_token - tags: test - name: Get Information about the UI Join Token - tags: test infoblox.bloxone.infra_join_token_info: filters: name: "{{ name }}" @@ -91,7 +85,7 @@ - ui_join_token is changed - ui_join_token_info is not failed - ui_join_token_info.objects | length == 0 - tags: test + - name: Delete the UI Join Token (idempotent) infoblox.bloxone.infra_join_token: @@ -103,7 +97,7 @@ - ui_join_token is not changed - ui_join_token is not failed - - name: Create an UI Join Token with Description + - name: Create a UI Join Token with Description infoblox.bloxone.infra_join_token: name: "{{ name2 }}" description: "Test UI Join Token" @@ -120,7 +114,7 @@ - ui_join_token_info is not failed - ui_join_token_info.objects | length == 1 - - name: Create an UI Join Token with Tags + - name: Create a UI Join Token with Tags infoblox.bloxone.infra_join_token: name: "{{ name2 }}" description: "Test UI Join Token" @@ -144,4 +138,4 @@ infoblox.bloxone.infra_join_token: name: "{{ name2 }}" state: absent - ignore_errors: true \ No newline at end of file + ignore_errors: true diff --git a/tests/integration/targets/infra_join_token_info/tasks/main.yml b/tests/integration/targets/infra_join_token_info/tasks/main.yml index cb97359..ee2a23f 100644 --- a/tests/integration/targets/infra_join_token_info/tasks/main.yml +++ b/tests/integration/targets/infra_join_token_info/tasks/main.yml @@ -9,7 +9,7 @@ name: "test-ui-join-token-{{ 999999 | random | string }}" tag_value: "site-{{ 999999 | random | string }}" - - name: Create an UI Join Token + - name: Create a UI Join Token infoblox.bloxone.infra_join_token: name: "{{ name }}" tags: @@ -36,7 +36,7 @@ - ui_join_token_info.objects | length == 1 - ui_join_token_info.objects[0].id == ui_join_token.id - - name: Get Information about the UI Join Token by tag filter + - name: Get Information about the UI Join Token by a tag filter infoblox.bloxone.infra_join_token_info: tag_filters: location: "{{ tag_value }}" @@ -51,4 +51,4 @@ infoblox.bloxone.infra_join_token: name: "{{ name }}" state: absent - ignore_errors: true \ No newline at end of file + ignore_errors: true From 98d9fc4aea3357897104c454e97a9d87f2e743a1 Mon Sep 17 00:00:00 2001 From: Ujjwal Nasra <125353741+unasra@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:12:39 +0530 Subject: [PATCH 4/7] addressed review comments --- plugins/modules/infra_join_token.py | 44 ++++--- plugins/modules/infra_join_token_info.py | 18 +-- .../targets/infra_join_token/tasks/main.yml | 116 +++++++++--------- .../infra_join_token_info/tasks/main.yml | 32 ++--- 4 files changed, 107 insertions(+), 103 deletions(-) diff --git a/plugins/modules/infra_join_token.py b/plugins/modules/infra_join_token.py index c862027..e7e58e6 100644 --- a/plugins/modules/infra_join_token.py +++ b/plugins/modules/infra_join_token.py @@ -11,9 +11,9 @@ DOCUMENTATION = r""" --- module: infra_join_token -short_description: Manage UIJoinToken +short_description: Manage JoinToken description: - - Manage UIJoinToken + - Manage JoinToken version_added: 2.0.0 author: Infoblox Inc. (@infobloxopen) options: @@ -29,19 +29,23 @@ required: false choices: - present - - absent + - revoked default: present description: - description: "" + description: + - Description of the Join Token type: str expires_at: - description: "" + description: + - Expiration time of the Join Token type: str name: - description: "" + description: + - Name of the Join Token type: str tags: - description: "" + description: + - Tags of the Join Token type: dict extends_documentation_fragment: @@ -49,22 +53,22 @@ """ # noqa: E501 EXAMPLES = r""" - - name: Create a UI Join token + - name: Create a Join token infoblox.bloxone.infra_join_token: name: "example_token" state: "present" - - name: Create a UI Join Token with Additional Fields + - name: Create a Join Token with Additional Fields infoblox.bloxone.infra_join_token: name: "example_token" description: "Example Join Token" tags: location: "my-location" - - name: Delete a UI Join token + - name: Revoke a Join token infoblox.bloxone.infra_join_token: name: "example_token" - state: "absent" + state: "revoked" """ from ansible_collections.infoblox.bloxone.plugins.module_utils.modules import BloxoneAnsibleModule @@ -76,9 +80,9 @@ pass # Handled by BloxoneAnsibleModule -class UIJoinTokenModule(BloxoneAnsibleModule): +class JoinTokenModule(BloxoneAnsibleModule): def __init__(self, *args, **kwargs): - super(UIJoinTokenModule, self).__init__(*args, **kwargs) + super(JoinTokenModule, self).__init__(*args, **kwargs) exclude = ["state", "csp_url", "api_key", "id"] self._payload_params = {k: v for k, v in self.params.items() if v is not None and k not in exclude} @@ -114,7 +118,7 @@ def find(self): resp = UIJoinTokenApi(self.client).read(self.params["id"]) return resp.result except NotFoundException as e: - if self.params["state"] == "absent": + if self.params["state"] == "revoked": return None raise e else: @@ -171,16 +175,16 @@ def run_command(self): if self.params["state"] == "present" and self.existing is None: item = self.create() result["changed"] = True - result["msg"] = "UIJoinToken created" + result["msg"] = "JoinToken created" elif self.params["state"] == "present" and self.existing is not None: if self.payload_changed(): item = self.update() result["changed"] = True - result["msg"] = "UIJoinToken updated" - elif self.params["state"] == "absent" and self.existing is not None: + result["msg"] = "JoinToken updated" + elif self.params["state"] == "revoked" and self.existing is not None: self.delete() result["changed"] = True - result["msg"] = "UIJoinToken deleted" + result["msg"] = "JoinToken Revoked" if self.check_mode: # if in check mode, do not update the result or the diff, just return the changed state @@ -203,14 +207,14 @@ def run_command(self): def main(): module_args = dict( id=dict(type="str", required=False), - state=dict(type="str", required=False, choices=["present", "absent"], default="present"), + state=dict(type="str", required=False, choices=["present", "revoked"], default="present"), description=dict(type="str"), expires_at=dict(type="str"), name=dict(type="str"), tags=dict(type="dict"), ) - module = UIJoinTokenModule( + module = JoinTokenModule( argument_spec=module_args, supports_check_mode=True, required_if=[("state", "present", ["name"])], diff --git a/plugins/modules/infra_join_token_info.py b/plugins/modules/infra_join_token_info.py index 3c633a4..ca95f14 100644 --- a/plugins/modules/infra_join_token_info.py +++ b/plugins/modules/infra_join_token_info.py @@ -10,9 +10,9 @@ DOCUMENTATION = r""" --- module: infra_join_token_info -short_description: Manage UIJoinToken +short_description: Manage JoinToken description: - - Manage UIJoinToken + - Manage JoinToken version_added: 2.0.0 author: Infoblox Inc. (@infobloxopen) options: @@ -47,20 +47,20 @@ """ # noqa: E501 EXAMPLES = r""" - - name: Get UI Join Token information by ID + - name: Get Join Token information by ID infoblox.bloxone.infra_join_token_info: id: "{{ join_token_id }}" - - name: Get a UI Join Token information by filters (e.g. name) + - name: Get a Join Token information by filters (e.g. name) infoblox.bloxone.infra_join_token_info: filters: name: "example_token" - - name: Get a UI Join Token information by raw filter query + - name: Get a Join Token information by raw filter query infoblox.bloxone.infra_join_token_info: filter_query: "name=='example_token'" - - name: Get a UI Join Token information by tag filters + - name: Get a Join Token information by tag filters infoblox.bloxone.infra_join_token_info: tag_filters: location: "site-1" @@ -75,9 +75,9 @@ pass # Handled by BloxoneAnsibleModule -class UIJoinTokenInfoModule(BloxoneAnsibleModule): +class JoinTokenInfoModule(BloxoneAnsibleModule): def __init__(self, *args, **kwargs): - super(UIJoinTokenInfoModule, self).__init__(*args, **kwargs) + super(JoinTokenInfoModule, self).__init__(*args, **kwargs) self._existing = None self._limit = 1000 @@ -157,7 +157,7 @@ def main(): tag_filter_query=dict(type="str", required=False), ) - module = UIJoinTokenInfoModule( + module = JoinTokenInfoModule( argument_spec=module_args, supports_check_mode=True, mutually_exclusive=[ diff --git a/tests/integration/targets/infra_join_token/tasks/main.yml b/tests/integration/targets/infra_join_token/tasks/main.yml index 126c719..e12d2be 100644 --- a/tests/integration/targets/infra_join_token/tasks/main.yml +++ b/tests/integration/targets/infra_join_token/tasks/main.yml @@ -7,135 +7,135 @@ block: # Create a random name to avoid conflicts - ansible.builtin.set_fact: - name: "test-ui-join-token-{{ 999999 | random | string }}" - name2: "test-ui-join-token-{{ 999999 | random | string }}" + name: "test-join-token-{{ 999999 | random | string }}" + name2: "test-join-token-{{ 999999 | random | string }}" - - name: Create a UI Join Token (check mode) + - name: Create a Join Token (check mode) infoblox.bloxone.infra_join_token: name: "{{ name }}" state: present check_mode: true - register: ui_join_token - - name: Get Information about the UI Join Token + register: join_token + - name: Get Information about the Join Token infoblox.bloxone.infra_join_token_info: filters: name: "{{ name }}" - register: ui_join_token_info + register: join_token_info - assert: that: - - ui_join_token is changed - - ui_join_token_info is not failed - - ui_join_token_info.objects | length == 0 + - join_token is changed + - join_token_info is not failed + - join_token_info.objects | length == 0 - - name: Create a UI Join Token + - name: Create a Join Token infoblox.bloxone.infra_join_token: name: "{{ name }}" state: present - register: ui_join_token - - name: Get Information about the UI Join Token + register: join_token + - name: Get Information about the Join Token infoblox.bloxone.infra_join_token_info: filters: name: "{{ name }}" - register: ui_join_token_info + register: join_token_info - assert: that: - - ui_join_token is changed - - ui_join_token_info is not failed - - ui_join_token_info.objects | length == 1 + - join_token is changed + - join_token_info is not failed + - join_token_info.objects | length == 1 - - name: Create a UI Join Token (idempotent) + - name: Create a Join Token (idempotent) infoblox.bloxone.infra_join_token: name: "{{ name }}" state: present - register: ui_join_token + register: join_token - assert: that: - - ui_join_token is not changed - - ui_join_token is not failed + - join_token is not changed + - join_token is not failed - - name: Delete the UI Join Token (check mode) + - name: Delete the Join Token (check mode) infoblox.bloxone.infra_join_token: name: "{{ name }}" - state: absent + state: revoked check_mode: true - register: ui_join_token - - name: Get Information about the UI Join Token + register: join_token + - name: Get Information about the Join Token infoblox.bloxone.infra_join_token_info: filters: name: "{{ name }}" - register: ui_join_token_info + register: join_token_info - assert: that: - - ui_join_token is changed - - ui_join_token_info is not failed - - ui_join_token_info.objects | length == 1 + - join_token is changed + - join_token_info is not failed + - join_token_info.objects | length == 1 - - name: Delete the UI Join Token + - name: Delete the Join Token infoblox.bloxone.infra_join_token: name: "{{ name }}" - state: absent - register: ui_join_token - - name: Get Information about the UI Join Token + state: revoked + register: join_token + - name: Get Information about the Join Token infoblox.bloxone.infra_join_token_info: filters: name: "{{ name }}" - register: ui_join_token_info + register: join_token_info - assert: that: - - ui_join_token is changed - - ui_join_token_info is not failed - - ui_join_token_info.objects | length == 0 + - join_token is changed + - join_token_info is not failed + - join_token_info.objects | length == 0 - - name: Delete the UI Join Token (idempotent) + - name: Delete the Join Token (idempotent) infoblox.bloxone.infra_join_token: name: "{{ name }}" - state: absent - register: ui_join_token + state: revoked + register: join_token - assert: that: - - ui_join_token is not changed - - ui_join_token is not failed + - join_token is not changed + - join_token is not failed - - name: Create a UI Join Token with Description + - name: Create a Join Token with Description infoblox.bloxone.infra_join_token: name: "{{ name2 }}" - description: "Test UI Join Token" + description: "Test Join Token" state: present - register: ui_join_token - - name: Get Information about the UI Join Token + register: join_token + - name: Get Information about the Join Token infoblox.bloxone.infra_join_token_info: filters: name: "{{ name2 }}" - register: ui_join_token_info + register: join_token_info - assert: that: - - ui_join_token is changed - - ui_join_token_info is not failed - - ui_join_token_info.objects | length == 1 + - join_token is changed + - join_token_info is not failed + - join_token_info.objects | length == 1 - - name: Create a UI Join Token with Tags + - name: Create a Join Token with Tags infoblox.bloxone.infra_join_token: name: "{{ name2 }}" - description: "Test UI Join Token" + description: "Test Join Token" tags: location: "site-1" state: present - register: ui_join_token - - name: Get Information about the UI Join Token + register: join_token + - name: Get Information about the Join Token infoblox.bloxone.infra_join_token_info: filters: name: "{{ name2 }}" - register: ui_join_token_info + register: join_token_info - assert: that: - - ui_join_token is changed - - ui_join_token_info is not failed - - ui_join_token_info.objects | length == 1 + - join_token is changed + - join_token_info is not failed + - join_token_info.objects | length == 1 always: - name: "Clean up the Join Token" infoblox.bloxone.infra_join_token: name: "{{ name2 }}" - state: absent + state: revoked ignore_errors: true diff --git a/tests/integration/targets/infra_join_token_info/tasks/main.yml b/tests/integration/targets/infra_join_token_info/tasks/main.yml index ee2a23f..a516819 100644 --- a/tests/integration/targets/infra_join_token_info/tasks/main.yml +++ b/tests/integration/targets/infra_join_token_info/tasks/main.yml @@ -6,49 +6,49 @@ block: - ansible.builtin.set_fact: - name: "test-ui-join-token-{{ 999999 | random | string }}" + name: "test-join-token-{{ 999999 | random | string }}" tag_value: "site-{{ 999999 | random | string }}" - - name: Create a UI Join Token + - name: Create a Join Token infoblox.bloxone.infra_join_token: name: "{{ name }}" tags: location: "{{ tag_value }}" state: present - register: ui_join_token + register: join_token - - name: Get Information about the UI Join Token by filters (Name) + - name: Get Information about the Join Token by filters (Name) infoblox.bloxone.infra_join_token_info: filters: name: "{{ name }}" - register: ui_join_token_info + register: join_token_info - assert: that: - - ui_join_token_info.objects | length == 1 - - ui_join_token_info.objects[0].id == ui_join_token.id + - join_token_info.objects | length == 1 + - join_token_info.objects[0].id == join_token.id - - name: Get Information about the UI Join Token by filter query + - name: Get Information about the Join Token by filter query infoblox.bloxone.infra_join_token_info: filter_query: "name=='{{ name }}'" - register: ui_join_token_info + register: join_token_info - assert: that: - - ui_join_token_info.objects | length == 1 - - ui_join_token_info.objects[0].id == ui_join_token.id + - join_token_info.objects | length == 1 + - join_token_info.objects[0].id == join_token.id - - name: Get Information about the UI Join Token by a tag filter + - name: Get Information about the Join Token by a tag filter infoblox.bloxone.infra_join_token_info: tag_filters: location: "{{ tag_value }}" - register: ui_join_token_info + register: join_token_info - assert: that: - - ui_join_token_info.objects | length == 1 - - ui_join_token_info.objects[0].id == ui_join_token.id + - join_token_info.objects | length == 1 + - join_token_info.objects[0].id == join_token.id always: - name: "Clean up the Join Token" infoblox.bloxone.infra_join_token: name: "{{ name }}" - state: absent + state: revoked ignore_errors: true From b2117b067d672a0280a0287bcd470c949353158e Mon Sep 17 00:00:00 2001 From: Ujjwal Nasra <125353741+unasra@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:16:20 +0530 Subject: [PATCH 5/7] added conditional check for revoked tokens --- plugins/modules/infra_join_token.py | 11 +++++------ plugins/modules/infra_join_token_info.py | 5 ----- .../targets/infra_join_token/tasks/main.yml | 3 ++- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/plugins/modules/infra_join_token.py b/plugins/modules/infra_join_token.py index e7e58e6..7903d2b 100644 --- a/plugins/modules/infra_join_token.py +++ b/plugins/modules/infra_join_token.py @@ -129,11 +129,6 @@ def find(self): if not resp.results: resp.results = [] - # If result is REVOKED, remove it from the list - for i in resp.results: - if i.status == "REVOKED": - resp.results.pop(resp.results.index(i)) - if len(resp.results) == 1: return resp.results[0] if len(resp.results) > 1: @@ -181,10 +176,14 @@ def run_command(self): item = self.update() result["changed"] = True result["msg"] = "JoinToken updated" - elif self.params["state"] == "revoked" and self.existing is not None: + elif self.params["state"] == "revoked" and self.existing is not None and self.existing.status != "REVOKED": self.delete() result["changed"] = True result["msg"] = "JoinToken Revoked" + elif self.params["state"] == "revoked" and self.existing is not None and self.existing.status == "REVOKED": + self.delete() + result["changed"] = False + result["msg"] = "JoinToken Revoked" if self.check_mode: # if in check mode, do not update the result or the diff, just return the changed state diff --git a/plugins/modules/infra_join_token_info.py b/plugins/modules/infra_join_token_info.py index ca95f14..f524435 100644 --- a/plugins/modules/infra_join_token_info.py +++ b/plugins/modules/infra_join_token_info.py @@ -115,11 +115,6 @@ def find(self): if not resp.results: resp.results = [] - # Removes REVOKED tokens from the list - for i in resp.results: - if i.status == "REVOKED": - resp.results.pop(resp.results.index(i)) - all_results.extend(resp.results) if len(resp.results) < self._limit: diff --git a/tests/integration/targets/infra_join_token/tasks/main.yml b/tests/integration/targets/infra_join_token/tasks/main.yml index e12d2be..b105578 100644 --- a/tests/integration/targets/infra_join_token/tasks/main.yml +++ b/tests/integration/targets/infra_join_token/tasks/main.yml @@ -84,7 +84,8 @@ that: - join_token is changed - join_token_info is not failed - - join_token_info.objects | length == 0 + # The join token is revoked , not deleted , hence the length would be 1 + - join_token_info.objects | length == 1 - name: Delete the Join Token (idempotent) From 1ab73f959689a5dbb766be6f9d3ec66efe9e7c99 Mon Sep 17 00:00:00 2001 From: Ujjwal Nasra <125353741+unasra@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:09:19 +0530 Subject: [PATCH 6/7] fixed review comments --- plugins/modules/infra_join_token.py | 1 - .../targets/infra_join_token/tasks/main.yml | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/plugins/modules/infra_join_token.py b/plugins/modules/infra_join_token.py index 7903d2b..8aa062f 100644 --- a/plugins/modules/infra_join_token.py +++ b/plugins/modules/infra_join_token.py @@ -181,7 +181,6 @@ def run_command(self): result["changed"] = True result["msg"] = "JoinToken Revoked" elif self.params["state"] == "revoked" and self.existing is not None and self.existing.status == "REVOKED": - self.delete() result["changed"] = False result["msg"] = "JoinToken Revoked" diff --git a/tests/integration/targets/infra_join_token/tasks/main.yml b/tests/integration/targets/infra_join_token/tasks/main.yml index b105578..a80ba47 100644 --- a/tests/integration/targets/infra_join_token/tasks/main.yml +++ b/tests/integration/targets/infra_join_token/tasks/main.yml @@ -53,7 +53,7 @@ - join_token is not changed - join_token is not failed - - name: Delete the Join Token (check mode) + - name: Revoke the Join Token (check mode) infoblox.bloxone.infra_join_token: name: "{{ name }}" state: revoked @@ -69,8 +69,9 @@ - join_token is changed - join_token_info is not failed - join_token_info.objects | length == 1 + - join_token_info.objects[0].status == "ACTIVE" - - name: Delete the Join Token + - name: Revoke the Join Token infoblox.bloxone.infra_join_token: name: "{{ name }}" state: revoked @@ -86,9 +87,10 @@ - join_token_info is not failed # The join token is revoked , not deleted , hence the length would be 1 - join_token_info.objects | length == 1 + - join_token_info.objects[0].status == "REVOKED" - - name: Delete the Join Token (idempotent) + - name: Revoke the Join Token (idempotent) infoblox.bloxone.infra_join_token: name: "{{ name }}" state: revoked @@ -135,8 +137,14 @@ - join_token_info.objects | length == 1 always: - - name: "Clean up the Join Token" + - name: "Clean up the second Join Token" infoblox.bloxone.infra_join_token: name: "{{ name2 }}" state: revoked ignore_errors: true + + - name: "Clean up the first Join Token" + infoblox.bloxone.infra_join_token: + name: "{{ name }}" + state: revoked + ignore_errors: true \ No newline at end of file From 2156cbbc6777a90ba5a7f10d19559aaf288c5e88 Mon Sep 17 00:00:00 2001 From: Ujjwal Nasra <125353741+unasra@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:10:33 +0530 Subject: [PATCH 7/7] added new line --- tests/integration/targets/infra_join_token/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/infra_join_token/tasks/main.yml b/tests/integration/targets/infra_join_token/tasks/main.yml index a80ba47..93fa0c1 100644 --- a/tests/integration/targets/infra_join_token/tasks/main.yml +++ b/tests/integration/targets/infra_join_token/tasks/main.yml @@ -147,4 +147,4 @@ infoblox.bloxone.infra_join_token: name: "{{ name }}" state: revoked - ignore_errors: true \ No newline at end of file + ignore_errors: true