-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Infra Join token Support for Bloxone Ansible v2 #47
base: v2
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,223 @@ | ||||||
#!/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: "" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a lot of this description is empty. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added Descriptions |
||||||
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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
same for the below examples as well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed |
||||||
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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it might be better to explicitly state, we are revoking the token, instead of deleting. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
infoblox.bloxone.infra_join_token: | ||||||
name: "example_token" | ||||||
state: "absent" | ||||||
""" | ||||||
|
||||||
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): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion to keep the names matching the module name
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
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 | ||||||
unasra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
if not resp.results: | ||||||
resp.results = [] | ||||||
|
||||||
# If result is REVOKED, remove it from the list | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The LIST call returns the "REVOKED" tokens too . This operation has been added to eliminate the revoked tokens There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The UI shows it as well, maybe we don't have to explicitly filter them out here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is implemented , assertion for idempotency would fail ( see comment below ) |
||||||
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() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the choices here should be
present
andrevoked
as deletion is not allowed for join tokens.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added