123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- #!/usr/bin/env python
- import argparse
- import redis
- import flask
- import pytz
- from datetime import timedelta, datetime
- import dateutil.parser
- from gevent.pywsgi import WSGIServer
- from flask import Flask, jsonify
- from flask_cors import CORS, cross_origin
- app = Flask(__name__)
- CORS(app)
- timezone = pytz.timezone("UTC")
- EPOCH = timezone.localize(datetime(1970, 1, 1, 0, 0, 0))
- REDIS_POOL = None
- SCAN_TYPE_SCRIPT = """local cursor, pat, typ, cnt = ARGV[1], ARGV[2], ARGV[3], ARGV[4] or 100
- local rep = {}
- local res = redis.call('SCAN', cursor, 'MATCH', pat, 'COUNT', cnt)
- while #res[2] > 0 do
- local k = table.remove(res[2])
- local t = redis.call('TYPE', k)
- if t['ok'] == typ then
- table.insert(rep, k)
- end
- end
- rep = {tonumber(res[1]), rep}
- return rep"""
- @app.route('/')
- @cross_origin()
- def hello_world():
- return 'OK'
- @app.route('/search', methods=["POST", 'GET'])
- @cross_origin()
- def search():
- redis_client = redis.Redis(connection_pool=REDIS_POOL)
- result = []
- cursor = 0
- while True:
- cursor, keys = redis_client.eval(SCAN_TYPE_SCRIPT, 0, cursor, "*", "TSDB-TYPE", 100)
- result.extend([k.decode("ascii") for k in keys])
- if cursor == 0:
- break
- return jsonify(result)
- def process_targets(targets, redis_client):
- result = []
- for target in targets:
- if '*' in target:
- result.extend([k.decode('ascii') for k in redis_client.keys(target)])
- else:
- result.append(target)
- return result
- @app.route('/query', methods=["POST", 'GET'])
- def query():
- request = flask.request.get_json()
- response = []
- # !!! dates 'from' and 'to' are expected to be in UTC, which is what Grafana provides here.
- # If not in UTC, use pytz to set to UTC timezone and subtract the utcoffset().
- # Time delta calculations should always be done in UTC to avoid pitfalls of daylight offset changes.
- stime = (dateutil.parser.parse(request['range']['from']) - EPOCH).total_seconds() * 1000
- etime = (dateutil.parser.parse(request['range']['to']) - EPOCH).total_seconds() * 1000
-
-
- redis_client = redis.Redis(connection_pool=REDIS_POOL)
- targets = process_targets([t['target'] for t in request['targets']], redis_client)
- for target in targets:
- args = ['ts.range', target, int(stime), int(etime)]
- if 'intervalMs' in request and request['intervalMs'] > 0:
- args += ['avg', int(request['intervalMs'])]
- print(args)
- redis_resp = redis_client.execute_command(*args)
- datapoints = [(float(x2.decode("ascii")), x1) for x1, x2 in redis_resp]
- response.append(dict(target=target, datapoints=datapoints))
- return jsonify(response)
- @app.route('/annotations')
- def annotations():
- return jsonify([])
- def main():
- global REDIS_POOL
- parser = argparse.ArgumentParser()
- parser.add_argument("--host", help="server address to listen to", default="0.0.0.0")
- parser.add_argument("--port", help="port number to listen to", default=8080, type=int)
- parser.add_argument("--redis-server", help="redis server address", default="localhost")
- parser.add_argument("--redis-port", help="redis server port", default=6379, type=int)
- args = parser.parse_args()
- REDIS_POOL = redis.ConnectionPool(host=args.redis_server, port=args.redis_port)
- http_server = WSGIServer(('', args.port), app)
- http_server.serve_forever()
- if __name__ == '__main__':
- main()
|