community.general/lib/ansible/plugins/callback/grafana_annotations.py
James Cassell bc4ef99533 standardize TLS connection properties (#54315)
* openstack: standardize tls params

* tower: tower_verify_ssl->validate_certs

* docker: use standard tls config params

- cacert_path -> ca_cert
- cert_path -> client_cert
- key_path -> client_key
- tls_verify -> validate_certs

* k8s: standardize tls connection params

- verify_ssl -> validate_certs
- ssl_ca_cert -> ca_cert
- cert_file -> client_cert
- key_file -> client_key

* ingate: verify_ssl -> validate_certs

* manageiq: standardize tls params

- verify_ssl -> validate_certs
- ca_bundle_path -> ca_cert

* mysql: standardize tls params

- ssl_ca -> ca_cert
- ssl_cert -> client_cert
- ssl_key -> client_key

* nios: ssl_verify -> validate_certs

* postgresql: ssl_rootcert -> ca_cert

* rabbitmq: standardize tls params

- cacert -> ca_cert
- cert -> client_cert
- key -> client_key

* rackspace: verify_ssl -> validate_certs

* vca: verify_certs -> validate_certs

* kubevirt_cdi_upload: upload_host_verify_ssl -> upload_host_validate_certs

* lxd: standardize tls params

- key_file -> client_key
- cert_file -> client_cert

* get_certificate: ca_certs -> ca_cert

* get_certificate.py: clarify one or more certs in a file

Co-Authored-By: jamescassell <code@james.cassell.me>

* zabbix: tls_issuer -> ca_cert

* bigip_device_auth_ldap: standardize tls params

- ssl_check_peer -> validate_certs
- ssl_client_cert -> client_cert
- ssl_client_key -> client_key
- ssl_ca_cert -> ca_cert

* vdirect: vdirect_validate_certs -> validate_certs

* mqtt: standardize tls params

- ca_certs -> ca_cert
- certfile -> client_cert
- keyfile -> client_key

* pulp_repo: standardize tls params

remove `importer_ssl` prefix

* rhn_register: sslcacert -> ca_cert

* yum_repository: standardize tls params

The fix for yum_repository is not straightforward since this module is
only a thin wrapper for the underlying commands and config.  In this
case, we add the new values as aliases, keeping the old as primary,
only due to the internal structure of the module.

Aliases added:
- sslcacert -> ca_cert
- sslclientcert -> client_cert
- sslclientkey -> client_key
- sslverify -> validate_certs

* gitlab_hook: enable_ssl_verification -> hook_validate_certs

* Adjust arguments for docker_swarm inventory plugin.

* foreman callback: standardize tls params

- ssl_cert -> client_cert
- ssl_key -> client_key

* grafana_annotations: validate_grafana_certs -> validate_certs

* nrdp callback: validate_nrdp_certs -> validate_certs

* kubectl connection: standardize tls params

- kubectl_cert_file -> client_cert
- kubectl_key_file -> client_key
- kubectl_ssl_ca_cert -> ca_cert
- kubectl_verify_ssl -> validate_certs

* oc connection: standardize tls params

- oc_cert_file -> client_cert
- oc_key_file -> client_key
- oc_ssl_ca_cert -> ca_cert
- oc_verify_ssl -> validate_certs

* psrp connection: cert_trust_path -> ca_cert

TODO: cert_validation -> validate_certs (multi-valued vs bool)

* k8s inventory: standardize tls params

- cert_file -> client_cert
- key_file -> client_key
- ca_cert -> ca_cert
- verify_ssl -> validate_certs

* openshift inventory: standardize tls params

- cert_file -> client_cert
- key_file -> client_key
- ca_cert -> ca_cert
- verify_ssl -> validate_certs

* tower inventory: verify_ssl -> validate_certs

* hashi_vault lookup: cacert -> ca_cert

* k8s lookup: standardize tls params

- cert_file -> client_cert
- key_file -> client_key
- ca_cert -> ca_cert
- verify_ssl -> validate_certs

* laps_passord lookup: cacert_file -> ca_cert

* changelog for TLS parameter standardization
2019-03-28 00:19:28 -05:00

266 lines
9.2 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import json
import socket
import getpass
from base64 import b64encode
from datetime import datetime
from ansible.module_utils._text import to_text
from ansible.module_utils.urls import open_url
from ansible.plugins.callback import CallbackBase
DOCUMENTATION = """
callback: grafana_annotations
callback_type: notification
short_description: send ansible events as annotations on charts to grafana over http api.
author: "Rémi REY (@rrey)"
description:
- This callback will report start, failed and stats events to Grafana as annotations (https://grafana.com)
version_added: "2.6"
requirements:
- whitelisting in configuration
options:
grafana_url:
description: Grafana annotations api URL
required: True
env:
- name: GRAFANA_URL
ini:
- section: callback_grafana_annotations
key: grafana_url
validate_certs:
description: validate the SSL certificate of the Grafana server. (For HTTPS url)
env:
- name: GRAFANA_VALIDATE_CERT
ini:
- section: callback_grafana_annotations
key: validate_grafana_certs
- section: callback_grafana_annotations
key: validate_certs
default: True
type: bool
aliases: [ validate_grafana_certs ]
http_agent:
description: The HTTP 'User-agent' value to set in HTTP requets.
env:
- name: HTTP_AGENT
ini:
- section: callback_grafana_annotations
key: http_agent
default: 'Ansible (grafana_annotations callback)'
grafana_api_key:
description: Grafana API key, allowing to authenticate when posting on the HTTP API.
If not provided, grafana_login and grafana_password will
be required.
env:
- name: GRAFANA_API_KEY
ini:
- section: callback_grafana_annotations
key: grafana_api_key
grafana_user:
description: Grafana user used for authentication. Ignored if grafana_api_key is provided.
env:
- name: GRAFANA_USER
ini:
- section: callback_grafana_annotations
key: grafana_user
default: ansible
grafana_password:
description: Grafana password used for authentication. Ignored if grafana_api_key is provided.
env:
- name: GRAFANA_PASSWORD
ini:
- section: callback_grafana_annotations
key: grafana_password
default: ansible
grafana_dashboard_id:
description: The grafana dashboard id where the annotation shall be created.
env:
- name: GRAFANA_DASHBOARD_ID
ini:
- section: callback_grafana_annotations
key: grafana_dashboard_id
grafana_panel_id:
description: The grafana panel id where the annotation shall be created.
env:
- name: GRAFANA_PANEL_ID
ini:
- section: callback_grafana_annotations
key: grafana_panel_id
"""
PLAYBOOK_START_TXT = """\
Started playbook {playbook}
From '{hostname}'
By user '{username}'
"""
PLAYBOOK_ERROR_TXT = """\
Playbook {playbook} Failure !
From '{hostname}'
By user '{username}'
'{task}' failed on {host}
debug: {result}
"""
PLAYBOOK_STATS_TXT = """\
Playbook {playbook}
Duration: {duration}
Status: {status}
From '{hostname}'
By user '{username}'
Result:
{summary}
"""
def to_millis(dt):
return int(dt.strftime('%s')) * 1000
class CallbackModule(CallbackBase):
"""
ansible grafana callback plugin
ansible.cfg:
callback_plugins = <path_to_callback_plugins_folder>
callback_whitelist = grafana_annotations
and put the plugin in <path_to_callback_plugins_folder>
"""
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'aggregate'
CALLBACK_NAME = 'grafana_annotations'
CALLBACK_NEEDS_WHITELIST = True
def __init__(self, display=None):
super(CallbackModule, self).__init__(display=display)
self.headers = {'Content-Type': 'application/json'}
self.force_basic_auth = False
self.hostname = socket.gethostname()
self.username = getpass.getuser()
self.start_time = datetime.now()
self.errors = 0
def set_options(self, task_keys=None, var_options=None, direct=None):
super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
self.grafana_api_key = self.get_option('grafana_api_key')
self.grafana_url = self.get_option('grafana_url')
self.validate_grafana_certs = self.get_option('validate_certs')
self.http_agent = self.get_option('http_agent')
self.grafana_user = self.get_option('grafana_user')
self.grafana_password = self.get_option('grafana_password')
self.dashboard_id = self.get_option('grafana_dashboard_id')
self.panel_id = self.get_option('grafana_panel_id')
if self.grafana_api_key:
self.headers['Authorization'] = "Bearer %s" % self.grafana_api_key
else:
self.force_basic_auth = True
if self.grafana_url is None:
self.disabled = True
self._display.warning('Grafana URL was not provided. The '
'Grafana URL can be provided using '
'the `GRAFANA_URL` environment variable.')
self._display.info('Grafana URL: %s' % self.grafana_url)
def v2_playbook_on_start(self, playbook):
self.playbook = playbook._file_name
text = PLAYBOOK_START_TXT.format(playbook=self.playbook, hostname=self.hostname,
username=self.username)
data = {
'time': to_millis(self.start_time),
'text': text,
'tags': ['ansible', 'ansible_event_start', self.playbook]
}
if self.dashboard_id:
data["dashboardId"] = int(self.dashboard_id)
if self.panel_id:
data["panelId"] = int(self.panel_id)
self._send_annotation(json.dumps(data))
def v2_playbook_on_stats(self, stats):
end_time = datetime.now()
duration = end_time - self.start_time
summarize_stat = {}
for host in stats.processed.keys():
summarize_stat[host] = stats.summarize(host)
status = "FAILED"
if self.errors == 0:
status = "OK"
text = PLAYBOOK_STATS_TXT.format(playbook=self.playbook, hostname=self.hostname,
duration=duration.total_seconds(),
status=status, username=self.username,
summary=json.dumps(summarize_stat))
data = {
'time': to_millis(self.start_time),
'timeEnd': to_millis(end_time),
'isRegion': True,
'text': text,
'tags': ['ansible', 'ansible_report', self.playbook]
}
if self.dashboard_id:
data["dashboardId"] = int(self.dashboard_id)
if self.panel_id:
data["panelId"] = int(self.panel_id)
self._send_annotation(json.dumps(data))
def v2_runner_on_failed(self, result, **kwargs):
text = PLAYBOOK_ERROR_TXT.format(playbook=self.playbook, hostname=self.hostname,
username=self.username, task=result._task,
host=result._host.name, result=self._dump_results(result._result))
data = {
'time': to_millis(datetime.now()),
'text': text,
'tags': ['ansible', 'ansible_event_failure', self.playbook]
}
self.errors += 1
if self.dashboard_id:
data["dashboardId"] = int(self.dashboard_id)
if self.panel_id:
data["panelId"] = int(self.panel_id)
self._send_annotation(json.dumps(data))
def _send_annotation(self, annotation):
try:
response = open_url(self.grafana_url, data=annotation, headers=self.headers,
method="POST",
validate_certs=self.validate_grafana_certs,
url_username=self.grafana_user, url_password=self.grafana_password,
http_agent=self.http_agent, force_basic_auth=self.force_basic_auth)
except Exception as e:
self._display.error(u'Could not submit message to Grafana: %s' % to_text(e))