diff --git a/plugins/module_utils/base.py b/plugins/module_utils/base.py index 7d51601d..4d2cb2a7 100644 --- a/plugins/module_utils/base.py +++ b/plugins/module_utils/base.py @@ -17,7 +17,9 @@ # Copyright: (c) 2019, RĂ©mi REY (@rrey) from __future__ import absolute_import, division, print_function -from ansible.module_utils.urls import url_argument_spec +from ansible.module_utils.urls import url_argument_spec, fetch_url + +import json __metaclass__ = type @@ -52,3 +54,36 @@ def grafana_required_together(): def grafana_mutually_exclusive(): return [["url_username", "grafana_api_key"]] + + +def grafana_send_request(self, module, url, grafana_url, data=None, headers=None, method="GET"): + self.module = module + if data is not None: + data = json.dumps(data, sort_keys=True) + if not headers: + headers = [] + + full_url = "{grafana_url}{path}".format(grafana_url=grafana_url, path=url) + resp, info = fetch_url( + module=self.module, url=full_url, data=data, headers=headers, method=method + ) + status_code = info["status"] + if status_code == 404: + return None + elif status_code == 401: + return self._module.fail_json( + failed=True, + msg="Unauthorized to perform action '%s' on '%s' header: %s" + % (method, full_url, self.headers), + ) + elif status_code == 403: + self._module.fail_json(failed=True, msg="Permission Denied") + elif status_code < 0: + self._module.fail_json(failed=True, msg=info["msg"]) + elif status_code == 200: + return self._module.from_json(resp.read()) + self._module.fail_json( + failed=True, + msg="Grafana API answered with HTTP %d" % status_code, + body=self._module.from_json(resp.read()), + ) diff --git a/plugins/modules/grafana_datasource.py b/plugins/modules/grafana_datasource.py index 9b1aea6a..0d135d09 100644 --- a/plugins/modules/grafana_datasource.py +++ b/plugins/modules/grafana_datasource.py @@ -521,11 +521,9 @@ "withCredentials": false } """ -import json - from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.six.moves.urllib.parse import quote -from ansible.module_utils.urls import fetch_url, basic_auth_header +from ansible.module_utils.urls import basic_auth_header from ansible_collections.community.grafana.plugins.module_utils import base @@ -724,41 +722,27 @@ def __init__(self, module): self.switch_organization(self.org_id) # }}} - def _send_request(self, url, data=None, headers=None, method="GET"): - if data is not None: - data = json.dumps(data, sort_keys=True) - if not headers: - headers = [] - - full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url( - self._module, full_url, data=data, headers=headers, method=method - ) - status_code = info["status"] - if status_code == 404: - return None - elif status_code == 401: - self._module.fail_json( - failed=True, - msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url), - ) - elif status_code == 403: - self._module.fail_json(failed=True, msg="Permission Denied") - elif status_code == 200: - return self._module.from_json(resp.read()) - self._module.fail_json( - failed=True, - msg="Grafana API answered with HTTP %d for url %s and data %s" - % (status_code, url, data), - ) - def switch_organization(self, org_id): url = "/api/user/using/%d" % org_id - self._send_request(url, headers=self.headers, method="POST") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="POST", + ) def organization_by_name(self, org_name): url = "/api/user/orgs" - organizations = self._send_request(url, headers=self.headers, method="GET") + organizations = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) orga = next((org for org in organizations if org["name"] == org_name)) if orga: return orga["orgId"] @@ -769,19 +753,49 @@ def organization_by_name(self, org_name): def datasource_by_name(self, name): url = "/api/datasources/name/%s" % quote(name, safe="") - return self._send_request(url, headers=self.headers, method="GET") + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) def delete_datasource(self, name): url = "/api/datasources/name/%s" % quote(name, safe="") - self._send_request(url, headers=self.headers, method="DELETE") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="DELETE", + ) def update_datasource(self, ds_id, data): url = "/api/datasources/%d" % ds_id - self._send_request(url, data=data, headers=self.headers, method="PUT") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=data, + method="PUT", + ) def create_datasource(self, data): url = "/api/datasources" - self._send_request(url, data=data, headers=self.headers, method="POST") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=data, + method="POST", + ) def setup_module_object(): diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index 73c437db..e9b66f2a 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -176,10 +176,8 @@ - 1 """ -import json - from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import fetch_url, basic_auth_header +from ansible.module_utils.urls import basic_auth_header from ansible_collections.community.grafana.plugins.module_utils import base from ansible.module_utils.six.moves.urllib.parse import quote from ansible.module_utils._text import to_text @@ -223,45 +221,27 @@ def __init__(self, module): failed=True, msg="Folders API is available starting Grafana v5" ) - def _send_request(self, url, data=None, headers=None, method="GET"): - if data is not None: - data = json.dumps(data, sort_keys=True) - if not headers: - headers = [] - - full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url( - self._module, full_url, data=data, headers=headers, method=method - ) - status_code = info["status"] - if status_code == 404: - return None - elif status_code == 401: - self._module.fail_json( - failed=True, - msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url), - ) - elif status_code == 403: - self._module.fail_json(failed=True, msg="Permission Denied") - elif status_code == 412: - error_msg = resp.read()["message"] - self._module.fail_json(failed=True, msg=error_msg) - elif status_code == 200: - # XXX: Grafana folders endpoint stopped sending back json in response for delete operations - # see https://github.com/grafana/grafana/issues/77673 - response = resp.read() or "{}" - return self._module.from_json(response) - self._module.fail_json( - failed=True, msg="Grafana Folders API answered with HTTP %d" % status_code - ) - def switch_organization(self, org_id): url = "/api/user/using/%d" % org_id - self._send_request(url, headers=self.headers, method="POST") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="POST", + ) def organization_by_name(self, org_name): url = "/api/user/orgs" - organizations = self._send_request(url, headers=self.headers, method="GET") + organizations = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) orga = next((org for org in organizations if org["name"] == org_name)) if orga: return orga["orgId"] @@ -272,8 +252,13 @@ def organization_by_name(self, org_name): def get_version(self): url = "/api/health" - response = self._send_request( - url, data=None, headers=self.headers, method="GET" + response = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", ) version = response.get("version") if version is not None: @@ -284,14 +269,26 @@ def get_version(self): def create_folder(self, title): url = "/api/folders" folder = dict(title=title) - response = self._send_request( - url, data=folder, headers=self.headers, method="POST" + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=folder, + method="POST", ) - return response def get_folder(self, title): url = "/api/search?type=dash-folder&query={title}".format(title=quote(title)) - response = self._send_request(url, headers=self.headers, method="GET") + response = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) for item in response: if item.get("title") == to_text(title): return item @@ -299,8 +296,14 @@ def get_folder(self, title): def delete_folder(self, folder_uid): url = "/api/folders/{folder_uid}".format(folder_uid=folder_uid) - response = self._send_request(url, headers=self.headers, method="DELETE") - return response + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="DELETE", + ) def main(): diff --git a/plugins/modules/grafana_organization.py b/plugins/modules/grafana_organization.py index 7bddcb3f..68155d8d 100644 --- a/plugins/modules/grafana_organization.py +++ b/plugins/modules/grafana_organization.py @@ -96,10 +96,8 @@ zipCode: "" """ -import json - from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import fetch_url, basic_auth_header +from ansible.module_utils.urls import basic_auth_header from ansible_collections.community.grafana.plugins.module_utils import base from ansible.module_utils.six.moves.urllib.parse import quote @@ -117,59 +115,44 @@ def __init__(self, module): # }}} self.grafana_url = base.clean_url(module.params.get("url")) - def _send_request(self, url, data=None, headers=None, method="GET"): - if data is not None: - data = json.dumps(data, sort_keys=True) - if not headers: - headers = [] - - full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url( - self._module, full_url, data=data, headers=headers, method=method - ) - status_code = info["status"] - if status_code == 404: - return None - elif status_code == 401: - self._module.fail_json( - failed=True, - msg="Unauthorized to perform action '%s' on '%s' header: %s" - % (method, full_url, self.headers), - ) - elif status_code == 403: - self._module.fail_json(failed=True, msg="Permission Denied") - elif status_code == 200: - return self._module.from_json(resp.read()) - if resp is None: - self._module.fail_json( - failed=True, - msg="Cannot connect to API Grafana %s" % info["msg"], - status=status_code, - url=info["url"], - ) - else: - self._module.fail_json( - failed=True, - msg="Grafana Org API answered with HTTP %d" % status_code, - body=self._module.from_json(resp.read()), - ) - def get_actual_org(self, name): # https://grafana.com/docs/grafana/latest/http_api/org/#get-organization-by-name url = "/api/orgs/name/{name}".format(name=quote(name)) - return self._send_request(url, headers=self.headers, method="GET") + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) def create_org(self, name): # https://grafana.com/docs/http_api/org/#create-organization url = "/api/orgs" org = dict(name=name) - self._send_request(url, data=org, headers=self.headers, method="POST") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=org, + method="POST", + ) return self.get_actual_org(name) def delete_org(self, org_id): # https://grafana.com/docs/http_api/org/#delete-organization url = "/api/orgs/{org_id}".format(org_id=org_id) - return self._send_request(url, headers=self.headers, method="DELETE") + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="DELETE", + ) def setup_module_object(): diff --git a/plugins/modules/grafana_silence.py b/plugins/modules/grafana_silence.py index 151b3659..28bf62fd 100644 --- a/plugins/modules/grafana_silence.py +++ b/plugins/modules/grafana_silence.py @@ -164,10 +164,8 @@ - "2023-07-27T13:27:33.042Z" """ -import json - from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import fetch_url, basic_auth_header +from ansible.module_utils.urls import basic_auth_header from ansible.module_utils._text import to_text from ansible_collections.community.grafana.plugins.module_utils import base @@ -205,38 +203,15 @@ def __init__(self, module): msg="Silences API is available starting with Grafana v8", ) - def _send_request(self, url, data=None, headers=None, method="GET"): - if data is not None: - data = json.dumps(data) - if not headers: - headers = [] - - full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url( - self._module, full_url, data=data, headers=headers, method=method - ) - status_code = info["status"] - if status_code == 404: - return None - elif status_code == 401: - self._module.fail_json( - failed=True, - msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url), - ) - elif status_code == 403: - self._module.fail_json(failed=True, msg="Permission Denied") - elif status_code in [200, 202]: - return self._module.from_json(resp.read()) - elif status_code == 400: - self._module.fail_json(failed=True, msg=info) - self._module.fail_json( - failed=True, msg="Grafana Silences API answered with HTTP %d" % status_code - ) - def get_version(self): url = "/api/health" - response = self._send_request( - url, data=None, headers=self.headers, method="GET" + response = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", ) version = response.get("version") if version is not None: @@ -253,8 +228,14 @@ def create_silence(self, comment, created_by, starts_at, ends_at, matchers): matchers=matchers, startsAt=starts_at, ) - response = self._send_request( - url, data=silence, headers=self.headers, method="POST" + response = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=silence, + method="POST", ) if self.get_version()["major"] == 8: response["silenceID"] = response["id"] @@ -264,7 +245,14 @@ def create_silence(self, comment, created_by, starts_at, ends_at, matchers): def get_silence(self, comment, created_by, starts_at, ends_at, matchers): url = "/api/alertmanager/grafana/api/v2/silences" - responses = self._send_request(url, headers=self.headers, method="GET") + responses = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) for response in responses: if ( @@ -281,20 +269,38 @@ def get_silence_by_id(self, silence_id): url = "/api/alertmanager/grafana/api/v2/silence/{SilenceId}".format( SilenceId=silence_id ) - response = self._send_request(url, headers=self.headers, method="GET") - return response + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) def get_silences(self): url = "/api/alertmanager/grafana/api/v2/silences" - response = self._send_request(url, headers=self.headers, method="GET") - return response + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) def delete_silence(self, silence_id): url = "/api/alertmanager/grafana/api/v2/silence/{SilenceId}".format( SilenceId=silence_id ) - response = self._send_request(url, headers=self.headers, method="DELETE") - return response + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="DELETE", + ) def setup_module_object(): diff --git a/plugins/modules/grafana_team.py b/plugins/modules/grafana_team.py index 76dc70a6..713dbea5 100644 --- a/plugins/modules/grafana_team.py +++ b/plugins/modules/grafana_team.py @@ -167,10 +167,8 @@ - 1 """ -import json - from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import fetch_url, basic_auth_header +from ansible.module_utils.urls import basic_auth_header from ansible.module_utils._text import to_text from ansible_collections.community.grafana.plugins.module_utils import base from ansible.module_utils.six.moves.urllib.parse import quote @@ -207,38 +205,15 @@ def __init__(self, module): failed=True, msg="Teams API is available starting Grafana v5" ) - def _send_request(self, url, data=None, headers=None, method="GET"): - if data is not None: - data = json.dumps(data, sort_keys=True) - if not headers: - headers = [] - - full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url( - self._module, full_url, data=data, headers=headers, method=method - ) - status_code = info["status"] - if status_code == 404: - return None - elif status_code == 401: - self._module.fail_json( - failed=True, - msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url), - ) - elif status_code == 403: - self._module.fail_json(failed=True, msg="Permission Denied") - elif status_code == 409: - self._module.fail_json(failed=True, msg="Team name is taken") - elif status_code == 200: - return self._module.from_json(resp.read()) - self._module.fail_json( - failed=True, msg="Grafana Teams API answered with HTTP %d" % status_code - ) - def get_version(self): url = "/api/health" - response = self._send_request( - url, data=None, headers=self.headers, method="GET" + response = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", ) version = response.get("version") if version is not None: @@ -249,14 +224,26 @@ def get_version(self): def create_team(self, name, email): url = "/api/teams" team = dict(email=email, name=name) - response = self._send_request( - url, data=team, headers=self.headers, method="POST" + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=team, + method="POST", ) - return response def get_team(self, name): url = "/api/teams/search?name={team}".format(team=quote(name)) - response = self._send_request(url, headers=self.headers, method="GET") + response = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) if not response.get("totalCount") <= 1: raise AssertionError("Expected 1 team, got %d" % response["totalCount"]) @@ -267,37 +254,77 @@ def get_team(self, name): def update_team(self, team_id, name, email): url = "/api/teams/{team_id}".format(team_id=team_id) team = dict(email=email, name=name) - response = self._send_request( - url, data=team, headers=self.headers, method="PUT" + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=team, + method="PUT", ) - return response def delete_team(self, team_id): url = "/api/teams/{team_id}".format(team_id=team_id) - response = self._send_request(url, headers=self.headers, method="DELETE") - return response + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="DELETE", + ) def get_team_members(self, team_id): url = "/api/teams/{team_id}/members".format(team_id=team_id) - response = self._send_request(url, headers=self.headers, method="GET") + response = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) members = [item.get("email") for item in response] return members def add_team_member(self, team_id, email): url = "/api/teams/{team_id}/members".format(team_id=team_id) data = {"userId": self.get_user_id_from_mail(email)} - self._send_request(url, data=data, headers=self.headers, method="POST") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=data, + method="POST", + ) def delete_team_member(self, team_id, email): user_id = self.get_user_id_from_mail(email) url = "/api/teams/{team_id}/members/{user_id}".format( team_id=team_id, user_id=user_id ) - self._send_request(url, headers=self.headers, method="DELETE") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="DELETE", + ) def get_user_id_from_mail(self, email): url = "/api/users/lookup?loginOrEmail={email}".format(email=quote(email)) - user = self._send_request(url, headers=self.headers, method="GET") + user = base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) if user is None: self._module.fail_json(failed=True, msg="User '%s' does not exists" % email) return user.get("id") diff --git a/plugins/modules/grafana_user.py b/plugins/modules/grafana_user.py index 6e99718a..515c6538 100644 --- a/plugins/modules/grafana_user.py +++ b/plugins/modules/grafana_user.py @@ -154,10 +154,8 @@ - false """ -import json - from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import fetch_url, basic_auth_header +from ansible.module_utils.urls import basic_auth_header from ansible_collections.community.grafana.plugins.module_utils import base from ansible.module_utils.six.moves.urllib.parse import quote @@ -175,35 +173,6 @@ def __init__(self, module): # }}} self.grafana_url = base.clean_url(module.params.get("url")) - def _send_request(self, url, data=None, headers=None, method="GET"): - if data is not None: - data = json.dumps(data, sort_keys=True) - if not headers: - headers = [] - - full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url( - self._module, full_url, data=data, headers=headers, method=method - ) - status_code = info["status"] - if status_code == 404: - return None - elif status_code == 401: - self._module.fail_json( - failed=True, - msg="Unauthorized to perform action '%s' on '%s' header: %s" - % (method, full_url, self.headers), - ) - elif status_code == 403: - self._module.fail_json(failed=True, msg="Permission Denied") - elif status_code == 200: - return self._module.from_json(resp.read()) - self._module.fail_json( - failed=True, - msg="Grafana Users API answered with HTTP %d" % status_code, - body=self._module.from_json(resp.read()), - ) - def create_user(self, name, email, login, password): # https://grafana.com/docs/http_api/admin/#global-users if not password: @@ -212,33 +181,69 @@ def create_user(self, name, email, login, password): ) url = "/api/admin/users" user = dict(name=name, email=email, login=login, password=password) - self._send_request(url, data=user, headers=self.headers, method="POST") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=user, + method="POST", + ) return self.get_user_from_login(login) def get_user_from_login(self, login): # https://grafana.com/docs/grafana/latest/http_api/user/#get-single-user-by-usernamelogin-or-email url = "/api/users/lookup?loginOrEmail={login}".format(login=quote(login)) - return self._send_request(url, headers=self.headers, method="GET") + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="GET", + ) def update_user(self, user_id, email, name, login): # https://grafana.com/docs/http_api/user/#user-update url = "/api/users/{user_id}".format(user_id=user_id) user = dict(email=email, name=name, login=login) - self._send_request(url, data=user, headers=self.headers, method="PUT") + base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=user, + method="PUT", + ) return self.get_user_from_login(login) def update_user_permissions(self, user_id, is_admin): # https://grafana.com/docs/http_api/admin/#permissions url = "/api/admin/users/{user_id}/permissions".format(user_id=user_id) permissions = dict(isGrafanaAdmin=is_admin) - return self._send_request( - url, data=permissions, headers=self.headers, method="PUT" + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + data=permissions, + method="PUT", ) def delete_user(self, user_id): # https://grafana.com/docs/http_api/admin/#delete-global-user url = "/api/admin/users/{user_id}".format(user_id=user_id) - return self._send_request(url, headers=self.headers, method="DELETE") + return base.grafana_send_request( + self, + module=self._module, + url=url, + grafana_url=self.grafana_url, + headers=self.headers, + method="DELETE", + ) def is_user_update_required(target_user, email, name, login, is_admin):