Add external http logging callback

Add callback to send VF logs and results in json
to a http server.

Related BZ:
https://bugzilla.redhat.com/show_bug.cgi?id=1902738

Change-Id: I09e0257daa1512698a2ec74ce2dca0e778aee966
This commit is contained in:
Mathieu Bultel 2021-01-27 23:53:46 +01:00
parent 87bc59af5b
commit 67a918d0e0
3 changed files with 181 additions and 1 deletions

View File

@ -1,4 +1,36 @@
==================
Validations-common
==================
A collection of common Ansible libraries and plugins for the Validation Framework
A collection of common Ansible libraries and plugins for the Validation
Framework
Validations Callbacks
=====================
******************
http_json callback
******************
The callback `http_json` sends Validations logs and information to an HTTP
server as a JSON format in order to get caught and analysed with external
tools for log parsing (as Fluentd or others).
This callback inherits from `validation_json` the format of the logging
remains the same as the other logger that the Validation Framework is using
by default.
To enable this callback, you need to add it to the callback whitelist.
Then you need to export your http server url and port::
export HTTP_JSON_SERVER=http://localhost
export HTTP_JSON_PORT=8989
The callback will post JSON log to the URL provided.
This repository has a simple HTTP server for testing purpose under::
tools/http_server.py
The default host and port are localhost and 8989, feel free to adjust those
values to your needs.

54
tools/http_server.py Normal file
View File

@ -0,0 +1,54 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
class SimpleHandler(BaseHTTPRequestHandler):
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_GET(self):
logging.info("Received GET request:\n"
"Headers: {}\n".format(str(self.headers)))
self._set_headers()
self.wfile.write("GET request: {}".format(self.path).encode('utf-8'))
def do_POST(self):
content_length = int(self.headers['Content-Length'])
data = self.rfile.read(content_length)
logging.info("Received POST request:\n"
"Headers: {}\n"
"Body: \n{}\n".format(self.headers, data.decode('utf-8')))
self._set_headers()
self.wfile.write("POST request: {}".format(self.path).encode('utf-8'))
def run(host='localhost', port=8989):
logging.basicConfig(level=logging.INFO)
http_server = HTTPServer((host, port), SimpleHandler)
logging.info("Starting http server...\n")
try:
http_server.serve_forever()
except KeyboardInterrupt:
pass
http_server.server_close()
logging.info('Stopping http server...\n')
if __name__ == '__main__':
run()

View File

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
__metaclass__ = type
DOCUMENTATION = '''
requirements:
- whitelist in configuration
short_description: sends JSON events to a HTTP server
description:
- This plugin logs ansible-playbook and ansible runs to an HTTP server in JSON format
options:
server:
description: remote server that will receive the event
env:
- name: HTTP_JSON_SERVER
default: http://localhost
ini:
- section: callback_http_json
key: http_json_server
port:
description: port on which the remote server is listening
env:
- name: HTTP_JSON_PORT
default: 8989
ini:
- section: callback_http_json
key: http_json_port
'''
import datetime
import json
import os
from urllib import request
from validations_common.callback_plugins import validation_json
url = '{}:{}'.format(os.getenv('HTTP_JSON_SERVER', 'http://localhost'),
os.getenv('HTTP_JSON_PORT', '8989'))
def http_post(data):
req = request.Request(url)
req.add_header('Content-Type', 'application/json; charset=utf-8')
json_data = json.dumps(data)
json_bytes = json_data.encode('utf-8')
req.add_header('Content-Length', len(json_bytes))
response = request.urlopen(req, json_bytes)
def current_time():
return '%sZ' % datetime.datetime.utcnow().isoformat()
class CallbackModule(validation_json.CallbackModule):
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'aggregate'
CALLBACK_NAME = 'http_json'
CALLBACK_NEEDS_WHITELIST = True
def __init__(self):
super(validation_json.CallbackModule, self).__init__()
self.results = []
self.simple_results = []
self.env = {}
self.t0 = None
self.current_time = current_time()
def v2_playbook_on_stats(self, stats):
"""Display info about playbook statistics"""
hosts = sorted(stats.processed.keys())
summary = {}
for h in hosts:
s = stats.summarize(h)
summary[h] = s
http_post({
'plays': self.results,
'stats': summary,
'validation_output': self.simple_results
})