diff --git a/lib/ansible/plugins/lookup/grafana_dashboard.py b/lib/ansible/plugins/lookup/grafana_dashboard.py new file mode 100644 index 00000000000..2c9729bbc43 --- /dev/null +++ b/lib/ansible/plugins/lookup/grafana_dashboard.py @@ -0,0 +1,178 @@ +# (c) 2018 Ansible Project +# 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 + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = """ +lookup: grafana_dashboard +author: Thierry Salle (@seuf) +version_added: "2.7" +short_description: list or search grafana dashboards +description: + - This lookup returns a list of grafana dashboards with possibility to filter them by query. +options: + grafana_url: + description: url of grafana. + env: + - name: GRAFANA_URL + default: http://127.0.0.1:3000 + grafana_api_key: + description: + - api key of grafana. + - when C(grafana_api_key) is set, the options C(grafan_user), C(grafana_password) and C(grafana_org_id) are ignored. + - Attention, please remove the two == at the end of the grafana_api_key + - because ansible lookup plugins options are splited on = (see example). + env: + - name: GRAFANA_API_KEY + grafana_user: + description: grafana authentication user. + env: + - name: GRAFANA_USER + default: admin + grafana_password: + description: grafana authentication password. + env: + - name: GRAFANA_PASSWORD + default: admin + grafana_org_id: + description: grafana organisation id. + env: + - name: GRAFANA_ORG_ID + default: 1 + search: + description: optional filter for dashboard search. + env: + - name: GRAFANA_DASHBOARD_SEARCH +""" + +EXAMPLES = """ +- name: get project foo grafana dashboards + set_fact: + grafana_dashboards: "{{ lookup('grafana_dashboard', 'grafana_url=http://grafana.company.com grafana_user=admin grafana_password=admin search=foo') }}" + +- name: get all grafana dashboards + set_fact: + grafana_dashboards: "{{ lookup('grafana_dashboard', 'grafana_url=http://grafana.company.com grafana_api_key=' ~ grafana_api_key|replace('==', '')) }}" +""" + +import base64 +import json +import os +from ansible.errors import AnsibleError, AnsibleParserError +from ansible.plugins.lookup import LookupBase +from ansible.module_utils.urls import open_url +from ansible.module_utils._text import to_bytes +from ansible.module_utils.six.moves.urllib.error import HTTPError + +try: + from __main__ import display +except ImportError: + from ansible.utils.display import Display + display = Display() + + +ANSIBLE_GRAFANA_URL = 'http://127.0.0.1:3000' +ANSIBLE_GRAFANA_API_KEY = None +ANSIBLE_GRAFANA_USER = 'admin' +ANSIBLE_GRAFANA_PASSWORD = 'admin' +ANSIBLE_GRAFANA_ORG_ID = 1 +ANSIBLE_GRAFANA_DASHBOARD_SEARCH = None + +if os.getenv('GRAFANA_URL') is not None: + ANSIBLE_GRAFANA_URL = os.environ['GRAFANA_URL'] + +if os.getenv('GRAFANA_API_KEY') is not None: + ANSIBLE_GRAFANA_API_KEY = os.environ['GRAFANA_API_KEY'] + +if os.getenv('GRAFANA_USER') is not None: + ANSIBLE_GRAFANA_USER = os.environ['GRAFANA_USER'] + +if os.getenv('GRAFANA_PASSWORD') is not None: + ANSIBLE_GRAFANA_PASSWORD = os.environ['GRAFANA_PASSWORD'] + +if os.getenv('GRAFANA_ORG_ID') is not None: + ANSIBLE_GRAFANA_ORG_ID = os.environ['GRAFANA_ORG_ID'] + +if os.getenv('GRAFANA_DASHBOARD_SEARCH') is not None: + ANSIBLE_GRAFANA_DASHBOARD_SEARCH = os.environ['GRAFANA_DASHBOARD_SEARCH'] + + +class GrafanaAPIException(Exception): + pass + + +class GrafanaAPI: + def __init__(self, **kwargs): + self.grafana_url = kwargs.get('grafana_url', ANSIBLE_GRAFANA_URL) + self.grafana_api_key = kwargs.get('grafana_api_key', ANSIBLE_GRAFANA_API_KEY) + self.grafana_user = kwargs.get('grafana_user', ANSIBLE_GRAFANA_USER) + self.grafana_password = kwargs.get('grafana_password', ANSIBLE_GRAFANA_PASSWORD) + self.grafana_org_id = kwargs.get('grafana_org_id', ANSIBLE_GRAFANA_ORG_ID) + self.search = kwargs.get('search', ANSIBLE_GRAFANA_DASHBOARD_SEARCH) + + def grafana_switch_organisation(self, headers): + try: + r = open_url('%s/api/user/using/%s' % (self.grafana_url, self.grafana_org_id), headers=headers, method='POST') + except HTTPError as e: + raise GrafanaAPIException('Unable to switch to organization %s : %s' % (self.grafana_org_id, str(e))) + if r.getcode() != 200: + raise GrafanaAPIException('Unable to switch to organization %s : %s' % (self.grafana_org_id, str(r.getcode()))) + + def grafana_headers(self): + headers = {'content-type': 'application/json; charset=utf8'} + if self.grafana_api_key: + headers['Authorization'] = "Bearer %s==" % self.grafana_api_key + else: + auth = base64.b64encode(to_bytes('%s:%s' % (self.grafana_user, self.grafana_password)).replace('\n', '')) + headers['Authorization'] = 'Basic %s' % auth + self.grafana_switch_organisation(headers) + + return headers + + def grafana_list_dashboards(self): + # define http headers + headers = self.grafana_headers() + + dashboard_list = [] + try: + if self.search: + r = open_url('%s/api/search?query=%s' % (self.grafana_url, self.search), headers=headers, method='GET') + else: + r = open_url('%s/api/search/' % self.grafana_url, headers=headers, method='GET') + except HTTPError as e: + raise GrafanaAPIException('Unable to search dashboards : %s' % str(e)) + if r.getcode() == 200: + try: + dashboard_list = json.loads(r.read()) + except Exception as e: + raise GrafanaAPIException('Unable to parse json list %s' % str(e)) + else: + raise GrafanaAPIException('Unable to list grafana dashboards : %s' % str(r.getcode())) + + return dashboard_list + + +class LookupModule(LookupBase): + + def run(self, terms, variables=None, **kwargs): + + grafana_args = terms[0].split(' ') + grafana_dict = {} + ret = [] + + for param in grafana_args: + try: + key, value = param.split('=') + except ValueError: + raise AnsibleError("grafana_dashboard lookup plugin needs key=value pairs, but received %s" % terms) + grafana_dict[key] = value + + grafana = GrafanaAPI(**grafana_dict) + + ret = grafana.grafana_list_dashboards() + + return ret