429 lines
14 KiB
Python
429 lines
14 KiB
Python
import decimal
|
|
import datetime
|
|
import json
|
|
|
|
from django.db.models import Q
|
|
from django.http import HttpResponse
|
|
from django.shortcuts import get_object_or_404
|
|
|
|
import datetime_to_decimal as dt
|
|
import models
|
|
import utils
|
|
|
|
SECS_PER_HOUR = 60 * 60
|
|
SECS_PER_DAY = SECS_PER_HOUR * 24
|
|
|
|
|
|
def get_event_names():
|
|
return models.RawData.objects.values('event').distinct()
|
|
|
|
|
|
def get_host_names():
|
|
# TODO: We need to upgrade to Django 1.4 so we can get tenent id and
|
|
# host and just do distinct on host name.
|
|
# like: values('host', 'tenant_id').distinct('host')
|
|
# This will be more meaningful. Host by itself isn't really.
|
|
return models.RawData.objects.values('host').distinct()
|
|
|
|
|
|
def routing_key_type(key):
|
|
if key.endswith('error'):
|
|
return 'E'
|
|
return ' '
|
|
|
|
|
|
def get_deployments():
|
|
return models.Deployment.objects.all().order_by('name')
|
|
|
|
|
|
def get_timings_for_uuid(uuid):
|
|
lifecycles = models.Lifecycle.objects.filter(instance=uuid)
|
|
|
|
results = [["?", "Event", "Time (secs)"]]
|
|
for lc in lifecycles:
|
|
timings = models.Timing.objects.filter(lifecycle=lc)
|
|
if not timings:
|
|
continue
|
|
for t in timings:
|
|
state = "?"
|
|
show_time = 'n/a'
|
|
if t.start_raw:
|
|
state = 'S'
|
|
if t.end_raw:
|
|
state = 'E'
|
|
if t.start_raw and t.end_raw:
|
|
state = "."
|
|
show_time = sec_to_time(t.diff)
|
|
results.append([state, t.name, show_time])
|
|
return results
|
|
|
|
|
|
def sec_to_time(diff):
|
|
seconds = int(diff)
|
|
usec = diff - seconds
|
|
days = seconds / SECS_PER_DAY
|
|
seconds -= (days * SECS_PER_DAY)
|
|
hours = seconds / SECS_PER_HOUR
|
|
seconds -= (hours * SECS_PER_HOUR)
|
|
minutes = seconds / 60
|
|
seconds -= (minutes * 60)
|
|
usec = str(usec)[1:4]
|
|
return "%dd %02d:%02d:%02d%s" % (days, hours, minutes, seconds, usec)
|
|
|
|
|
|
def rsp(data, status=200, content_type="application/json"):
|
|
return HttpResponse(data, content_type=content_type, status=status)
|
|
|
|
|
|
def error_response(status, type, message):
|
|
results = [["Error", "Message"], [type, message]]
|
|
return rsp(json.dumps(results), status)
|
|
|
|
|
|
def do_deployments(request):
|
|
deployments = get_deployments()
|
|
results = [["#", "Name"]]
|
|
for deployment in deployments:
|
|
results.append([deployment.id, deployment.name])
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_events(request):
|
|
events = get_event_names()
|
|
results = [["Event Name"]]
|
|
for event in events:
|
|
results.append([event['event']])
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_hosts(request):
|
|
hosts = get_host_names()
|
|
results = [["Host Name"]]
|
|
for host in hosts:
|
|
results.append([host['host']])
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_uuid(request):
|
|
uuid = str(request.GET['uuid'])
|
|
if not utils.is_uuid_like(uuid):
|
|
msg = "%s is not uuid-like" % uuid
|
|
return error_response(400, 'Bad Request', msg)
|
|
|
|
related = models.RawData.objects.select_related().filter(instance=uuid)\
|
|
.order_by('when')
|
|
results = [["#", "?", "When", "Deployment", "Event", "Host", "State",
|
|
"State'", "Task'"]]
|
|
for e in related:
|
|
when = dt.dt_from_decimal(e.when)
|
|
results.append([e.id, routing_key_type(e.routing_key), str(when),
|
|
e.deployment.name, e.event, e.host, e.state,
|
|
e.old_state, e.old_task])
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_timings_uuid(request):
|
|
uuid = request.GET['uuid']
|
|
if not utils.is_uuid_like(uuid):
|
|
msg = "%s is not uuid-like" % uuid
|
|
return error_response(400, 'Bad Request', msg)
|
|
results = get_timings_for_uuid(uuid)
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_timings(request):
|
|
name = request.GET['name']
|
|
results = [[name, "Time"]]
|
|
timings_query = models.Timing.objects.select_related()\
|
|
.filter(name=name)\
|
|
.exclude(Q(start_raw=None) | Q(end_raw=None))
|
|
if request.GET.get('end_when_min') is not None:
|
|
min_when = decimal.Decimal(request.GET['end_when_min'])
|
|
timings_query = timings_query.filter(end_when__gte=min_when)
|
|
if request.GET.get('end_when_max') is not None:
|
|
max_when = decimal.Decimal(request.GET['end_when_max'])
|
|
timings_query = timings_query.filter(end_when__lte=max_when)
|
|
timings = timings_query.order_by('diff')
|
|
|
|
for t in timings:
|
|
results.append([t.lifecycle.instance, sec_to_time(t.diff)])
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_summary(request):
|
|
events = get_event_names()
|
|
interesting = []
|
|
for e in events:
|
|
ev = e['event']
|
|
if ev.endswith('.start'):
|
|
interesting.append(ev[:-len('.start')])
|
|
|
|
results = [["Event", "N", "Min", "Max", "Avg"]]
|
|
|
|
for name in interesting:
|
|
timings = models.Timing.objects.filter(name=name) \
|
|
.exclude(Q(start_raw=None) | Q(end_raw=None)) \
|
|
.exclude(diff__lt=0)
|
|
if not timings:
|
|
continue
|
|
|
|
total, _min, _max = 0.0, None, None
|
|
num = len(timings)
|
|
for t in timings:
|
|
seconds = float(t.diff)
|
|
total += seconds
|
|
if _min is None:
|
|
_min = seconds
|
|
if _max is None:
|
|
_max = seconds
|
|
_min = min(_min, seconds)
|
|
_max = max(_max, seconds)
|
|
|
|
results.append([name, int(num), sec_to_time(_min),
|
|
sec_to_time(_max), sec_to_time(int(total / num))])
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_request(request):
|
|
request_id = request.GET['request_id']
|
|
if not utils.is_request_id_like(request_id):
|
|
msg = "%s is not request-id-like" % request_id
|
|
return error_response(400, 'Bad Request', msg)
|
|
|
|
events = models.RawData.objects.filter(request_id=request_id) \
|
|
.order_by('when')
|
|
results = [["#", "?", "When", "Deployment", "Event", "Host",
|
|
"State", "State'", "Task'"]]
|
|
for e in events:
|
|
when = dt.dt_from_decimal(e.when)
|
|
results.append([e.id, routing_key_type(e.routing_key), str(when),
|
|
e.deployment.name, e.event, e.host, e.state,
|
|
e.old_state, e.old_task])
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_show(request, event_id):
|
|
event_id = int(event_id)
|
|
results = []
|
|
event = None
|
|
try:
|
|
event = models.RawData.objects.get(id=event_id)
|
|
except models.RawData.ObjectDoesNotExist:
|
|
return results
|
|
|
|
results.append(["Key", "Value"])
|
|
results.append(["#", event.id])
|
|
when = dt.dt_from_decimal(event.when)
|
|
results.append(["When", str(when)])
|
|
results.append(["Deployment", event.deployment.name])
|
|
results.append(["Category", event.routing_key])
|
|
results.append(["Publisher", event.publisher])
|
|
results.append(["State", event.state])
|
|
results.append(["Event", event.event])
|
|
results.append(["Service", event.service])
|
|
results.append(["Host", event.host])
|
|
results.append(["UUID", event.instance])
|
|
results.append(["Req ID", event.request_id])
|
|
|
|
final = [results, ]
|
|
j = json.loads(event.json)
|
|
final.append(json.dumps(j, indent=2))
|
|
final.append(event.instance)
|
|
|
|
return rsp(json.dumps(final))
|
|
|
|
|
|
def do_watch(request, deployment_id):
|
|
deployment_id = int(deployment_id)
|
|
since = request.GET.get('since')
|
|
event_name = request.GET.get('event_name')
|
|
|
|
deployment_map = {}
|
|
for d in get_deployments():
|
|
deployment_map[d.id] = d
|
|
events = get_event_names()
|
|
max_event_width = max([len(event['event']) for event in events])
|
|
|
|
base_events = models.RawData.objects.order_by('when')
|
|
if deployment_id > 0:
|
|
base_events = base_events.filter(deployment=deployment_id)
|
|
|
|
if event_name:
|
|
base_events = base_events.filter(event=event_name)
|
|
|
|
# Ok, this may seem a little wonky, but I'm clamping the
|
|
# query time to the closest second. The implication is we
|
|
# may not return the absolute latest data (which will have
|
|
# to wait for the next query). The upside of doing this
|
|
# is we can effectively cache the responses. So, with a
|
|
# caching proxy server we can service a lot more clients
|
|
# without having to worry about microsecond differences
|
|
# causing cache misses.
|
|
|
|
now = datetime.datetime.utcnow()
|
|
now = now.replace(microsecond=0) # clamp it down.
|
|
dec_now = dt.dt_to_decimal(now)
|
|
if since:
|
|
since = decimal.Decimal(since)
|
|
else:
|
|
since = now - datetime.timedelta(seconds=2)
|
|
since = dt.dt_to_decimal(since)
|
|
base_events = base_events.filter(when__gt=since)
|
|
events = base_events.filter(when__lte=dec_now)
|
|
|
|
c = [10, 1, 15, 20, max_event_width, 36]
|
|
|
|
results = []
|
|
|
|
for raw in events:
|
|
uuid = raw.instance
|
|
if not uuid:
|
|
uuid = "-"
|
|
typ = routing_key_type(raw.routing_key)
|
|
when = dt.dt_from_decimal(raw.when)
|
|
results.append([raw.id, typ,
|
|
str(when.date()), str(when.time()),
|
|
deployment_map[raw.deployment.id].name,
|
|
raw.event,
|
|
uuid])
|
|
results_json = json.dumps([c, results, str(dec_now)])
|
|
|
|
return rsp(results_json)
|
|
|
|
|
|
def do_kpi(request, tenant_id=None):
|
|
if tenant_id:
|
|
if models.RawData.objects.filter(tenant=tenant_id).count() == 0:
|
|
message = "Could not find raws for tenant %s" % tenant_id
|
|
return error_response(404, 'Not Found', message)
|
|
|
|
yesterday = datetime.datetime.utcnow() - datetime.timedelta(days=1)
|
|
yesterday = dt.dt_to_decimal(yesterday)
|
|
trackers = models.RequestTracker.objects.select_related()\
|
|
.exclude(last_timing=None)\
|
|
.exclude(start__lt=yesterday)\
|
|
.order_by('duration')
|
|
|
|
results = [["Event", "Time", "UUID", "Deployment"]]
|
|
for track in trackers:
|
|
end_event = track.last_timing.end_raw
|
|
event = end_event.event[:-len(".end")]
|
|
uuid = track.lifecycle.instance
|
|
if tenant_id is None or (tenant_id == end_event.tenant):
|
|
results.append([event, sec_to_time(track.duration),
|
|
uuid, end_event.deployment.name])
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_list_usage_launches(request):
|
|
|
|
filter_args = {}
|
|
if 'instance' in request.GET:
|
|
uuid = request.GET['instance']
|
|
if not utils.is_uuid_like(uuid):
|
|
msg = "%s is not uuid-like" % uuid
|
|
return error_response(400, 'Bad Request', msg)
|
|
filter_args['instance'] = uuid
|
|
|
|
if len(filter_args) > 0:
|
|
launches = models.InstanceUsage.objects.filter(**filter_args)
|
|
else:
|
|
launches = models.InstanceUsage.objects.all()
|
|
|
|
results = [["UUID", "Launched At", "Instance Type Id"]]
|
|
|
|
for launch in launches:
|
|
launched = None
|
|
if launch.launched_at:
|
|
launched = str(dt.dt_from_decimal(launch.launched_at))
|
|
results.append([launch.instance, launched, launch.instance_type_id])
|
|
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_list_usage_deletes(request):
|
|
|
|
filter_args = {}
|
|
if 'instance' in request.GET:
|
|
uuid = request.GET['instance']
|
|
if not utils.is_uuid_like(uuid):
|
|
msg = "%s is not uuid-like" % uuid
|
|
return error_response(400, 'Bad Request', msg)
|
|
filter_args['instance'] = uuid
|
|
|
|
if len(filter_args) > 0:
|
|
deletes = models.InstanceDeletes.objects.filter(**filter_args)
|
|
else:
|
|
deletes = models.InstanceDeletes.objects.all()
|
|
|
|
results = [["UUID", "Launched At", "Deleted At"]]
|
|
|
|
for delete in deletes:
|
|
launched = None
|
|
if delete.launched_at:
|
|
launched = str(dt.dt_from_decimal(delete.launched_at))
|
|
deleted = None
|
|
if delete.deleted_at:
|
|
deleted = str(dt.dt_from_decimal(delete.deleted_at))
|
|
results.append([delete.instance, launched, deleted])
|
|
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_list_usage_exists(request):
|
|
|
|
filter_args = {}
|
|
if 'instance' in request.GET:
|
|
uuid = request.GET['instance']
|
|
if not utils.is_uuid_like(uuid):
|
|
msg = "%s is not uuid-like" % uuid
|
|
return error_response(400, 'Bad Request', msg)
|
|
filter_args['instance'] = uuid
|
|
|
|
if len(filter_args) > 0:
|
|
exists = models.InstanceExists.objects.filter(**filter_args)
|
|
else:
|
|
exists = models.InstanceExists.objects.all()
|
|
|
|
results = [["UUID", "Launched At", "Deleted At", "Instance Type Id",
|
|
"Message ID", "Status"]]
|
|
|
|
for exist in exists:
|
|
launched = None
|
|
if exist.launched_at:
|
|
launched = str(dt.dt_from_decimal(exist.launched_at))
|
|
deleted = None
|
|
if exist.deleted_at:
|
|
deleted = str(dt.dt_from_decimal(exist.deleted_at))
|
|
results.append([exist.instance, launched, deleted,
|
|
exist.instance_type_id, exist.message_id,
|
|
exist.status])
|
|
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_jsonreports(request):
|
|
yesterday = datetime.datetime.utcnow() - datetime.timedelta(days=1)
|
|
now = datetime.datetime.utcnow()
|
|
yesterday = dt.dt_to_decimal(yesterday)
|
|
now = dt.dt_to_decimal(now)
|
|
_from = request.GET.get('created_from', yesterday)
|
|
_to = request.GET.get('created_to', now)
|
|
reports = models.JsonReport.objects.filter(created__gte=_from,
|
|
created__lte=_to)
|
|
results = [['Id', 'Start', 'End', 'Created', 'Name', 'Version']]
|
|
for report in reports:
|
|
results.append([report.id,
|
|
float(dt.dt_to_decimal(report.period_start)),
|
|
float(dt.dt_to_decimal(report.period_end)),
|
|
float(report.created),
|
|
report.name,
|
|
report.version])
|
|
return rsp(json.dumps(results))
|
|
|
|
|
|
def do_jsonreport(request, report_id):
|
|
report_id = int(report_id)
|
|
report = get_object_or_404(models.JsonReport, pk=report_id)
|
|
return rsp(report.json)
|