graphing api response times

Adding timing functionality around all api calls
and outputting that to a graph on the dashboard itself.
This commit is contained in:
adriant
2014-12-17 14:49:28 +13:00
parent 120fca6c8d
commit 305cee63d5
5 changed files with 118 additions and 78 deletions

View File

@@ -167,32 +167,35 @@
color: rgba(255, 255, 255, 0.5); } color: rgba(255, 255, 255, 0.5); }
.widget-graph { .widget-graph {
background-color: #dc5945; background-color: #1A773F;
position: relative; } position: relative; }
.widget-graph svg { .widget-graph h2 {
position: absolute; font-size: 25px;
opacity: 0.4; white-space: pre; }
fill-opacity: 0.4; .widget-graph svg {
left: 0px; position: absolute;
top: 0px; } opacity: 0.6;
.widget-graph .title, .widget-graph .value { fill-opacity: 0.6;
position: relative; left: 0px;
z-index: 99; } top: 0px; }
.widget-graph .title { .widget-graph .title, .widget-graph .value {
color: rgba(255, 255, 255, 0.7); } position: relative;
.widget-graph .more-info { z-index: 99; }
color: rgba(255, 255, 255, 0.3); .widget-graph .title {
font-weight: 600; color: rgba(255, 255, 255, 0.7); }
font-size: 20px; .widget-graph .more-info {
margin-top: 0; } color: rgba(255, 255, 255, 0.3);
.widget-graph .x_tick { font-weight: 600;
position: absolute; font-size: 20px;
bottom: 0; } margin-top: 0; }
.widget-graph .x_tick .title { .widget-graph .x_tick {
font-size: 20px; position: absolute;
color: rgba(0, 0, 0, 0.4); bottom: 0; }
opacity: 0.5; .widget-graph .x_tick .title {
padding-bottom: 3px; } font-size: 20px;
color: rgba(0, 0, 0, 0.4);
opacity: 0.5;
padding-bottom: 3px; }
.widget-graph .y_ticks { .widget-graph .y_ticks {
font-size: 20px; font-size: 20px;
fill: rgba(0, 0, 0, 0.4); fill: rgba(0, 0, 0, 0.4);

View File

@@ -1,4 +1,8 @@
import collections import collections
import datetime
import json
from contextlib import contextmanager
import random import random
import nagios import nagios
from math import ceil from math import ceil
@@ -16,6 +20,8 @@ class BaseOpenstackSampler(DashieSampler):
def __init__(self, app, interval, conf=None, client_cache={}): def __init__(self, app, interval, conf=None, client_cache={}):
self._os_clients = client_cache self._os_clients = client_cache
self._conf = conf self._conf = conf
self._res_time_X = 0
self._response_times = collections.deque()
super(BaseOpenstackSampler, self).__init__(app, interval) super(BaseOpenstackSampler, self).__init__(app, interval)
def _convert(self, num): def _convert(self, num):
@@ -73,6 +79,46 @@ class BaseOpenstackSampler(DashieSampler):
return self._os_clients[region][service] return self._os_clients[region][service]
@contextmanager
def timed(self):
start = datetime.datetime.utcnow()
yield
end = datetime.datetime.utcnow()
self._api_response(int((end - start).total_seconds() * 1000))
def _api_response(self, ms):
self._response_times.append({'x': self._res_time_X,
'y': ms})
self._res_time_X += 1
if len(self._response_times) > 50:
self._response_times.popleft()
stats = {'min': -1, 'max': -1, 'avg': -1}
total = 0
for time in self._response_times:
total = total + time['y']
if time['y'] > stats['max']:
stats['max'] = time['y']
if stats['min'] == -1 or time['y'] < stats['min']:
stats['min'] = time['y']
stats['avg'] = int(total / len(self._response_times))
body = {}
body['displayedValue'] = ("min: %s max: %s avg: %s" %
(stats['min'], stats['max'],
stats['avg']))
body['points'] = list(self._response_times)
body['id'] = 'api_response'
body['updatedAt'] = (datetime.datetime.now().
strftime('%Y-%m-%d %H:%M:%S +0000'))
formatted_json = 'data: %s\n\n' % (json.dumps(body))
self._app.last_events['api_response'] = formatted_json
for event_queue in self._app.events_queue.values():
event_queue.put(formatted_json)
class CPUSampler(BaseOpenstackSampler): class CPUSampler(BaseOpenstackSampler):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -88,8 +134,10 @@ class CPUSampler(BaseOpenstackSampler):
for region in self._conf['allocation'].keys(): for region in self._conf['allocation'].keys():
nova = self._client('compute', region) nova = self._client('compute', region)
stats = nova.hypervisors.statistics() with self.timed():
hypervisors = nova.hypervisors.list() stats = nova.hypervisors.statistics()
with self.timed():
hypervisors = nova.hypervisors.list()
reserved = 0 reserved = 0
for hypervisor in hypervisors: for hypervisor in hypervisors:
@@ -124,8 +172,10 @@ class RAMSampler(BaseOpenstackSampler):
for region in self._conf['allocation'].keys(): for region in self._conf['allocation'].keys():
nova = self._client('compute', region) nova = self._client('compute', region)
stats = nova.hypervisors.statistics() with self.timed():
hypervisors = nova.hypervisors.list() stats = nova.hypervisors.statistics()
with self.timed():
hypervisors = nova.hypervisors.list()
reserved = 0 reserved = 0
for hypervisor in hypervisors: for hypervisor in hypervisors:
@@ -170,8 +220,10 @@ class IPSampler(BaseOpenstackSampler):
neutron = self._client('network', region) neutron = self._client('network', region)
ips = neutron.list_floatingips() with self.timed():
routers = neutron.list_routers() ips = neutron.list_floatingips()
with self.timed():
routers = neutron.list_routers()
net_gateways = 0 net_gateways = 0
for router in routers['routers']: for router in routers['routers']:
@@ -202,8 +254,10 @@ class RegionsCPUSampler(BaseOpenstackSampler):
for region in self._conf['allocation'].keys(): for region in self._conf['allocation'].keys():
nova = self._client('compute', region) nova = self._client('compute', region)
stats = nova.hypervisors.statistics() with self.timed():
hypervisors = nova.hypervisors.list() stats = nova.hypervisors.statistics()
with self.timed():
hypervisors = nova.hypervisors.list()
reserved = 0 reserved = 0
for hypervisor in hypervisors: for hypervisor in hypervisors:
@@ -232,8 +286,10 @@ class RegionsRAMSampler(BaseOpenstackSampler):
for region in self._conf['allocation'].keys(): for region in self._conf['allocation'].keys():
nova = self._client('compute', region) nova = self._client('compute', region)
stats = nova.hypervisors.statistics() with self.timed():
hypervisors = nova.hypervisors.list() stats = nova.hypervisors.statistics()
with self.timed():
hypervisors = nova.hypervisors.list()
reserved = 0 reserved = 0
for hypervisor in hypervisors: for hypervisor in hypervisors:
@@ -269,8 +325,10 @@ class RegionIPSampler(BaseOpenstackSampler):
for region in self._conf['allocation'].keys(): for region in self._conf['allocation'].keys():
neutron = self._client('network', region) neutron = self._client('network', region)
ips = neutron.list_floatingips() with self.timed():
routers = neutron.list_routers() ips = neutron.list_floatingips()
with self.timed():
routers = neutron.list_routers()
net_gateways = 0 net_gateways = 0
for router in routers['routers']: for router in routers['routers']:
@@ -368,18 +426,22 @@ class ResourceSampler(BaseOpenstackSampler):
nova = self._client('compute', region) nova = self._client('compute', region)
# cinder = self._client('storage', region) # cinder = self._client('storage', region)
stats = nova.hypervisors.statistics() with self.timed():
stats = nova.hypervisors.statistics()
resources['instances'] = resources['instances'] + stats.running_vms resources['instances'] = resources['instances'] + stats.running_vms
routers = neutron.list_routers() with self.timed():
routers = neutron.list_routers()
resources['routers'] = (resources['routers'] + resources['routers'] = (resources['routers'] +
len(routers['routers'])) len(routers['routers']))
networks = neutron.list_networks() with self.timed():
networks = neutron.list_networks()
resources['networks'] = (resources['networks'] + resources['networks'] = (resources['networks'] +
len(networks['networks'])) len(networks['networks']))
vpns = neutron.list_vpnservices() with self.timed():
vpns = neutron.list_vpnservices()
resources['vpns'] = (resources['vpns'] + resources['vpns'] = (resources['vpns'] +
len(vpns['vpnservices'])) len(vpns['vpnservices']))
@@ -392,33 +454,3 @@ class ResourceSampler(BaseOpenstackSampler):
items.append({'label': key, 'value': value}) items.append({'label': key, 'value': value})
return {'items': items} return {'items': items}
class ConvergenceSampler(BaseOpenstackSampler):
def name(self):
return 'convergence'
def __init__(self, *args, **kwargs):
self.seedX = 0
self.items = collections.deque()
super(ConvergenceSampler, self).__init__(*args, **kwargs)
def sample(self):
self.items.append({'x': self.seedX,
'y': random.randint(0, 20)})
self.seedX += 1
if len(self.items) > 10:
self.items.popleft()
return {'points': list(self.items)}
class UsageGaugeSampler(BaseOpenstackSampler):
def __init__(self, *args, **kwargs):
super(UsageGaugeSampler, self).__init__(*args, **kwargs)
def name(self):
return 'usage_gauge'
def sample(self):
return {'value': random.randint(0, 100), 'max': 100}

View File

@@ -60,9 +60,9 @@
<div data-id="ips_regions" data-view="ProgressBars" data-title="IPs provisioned per region"></div> <div data-id="ips_regions" data-view="ProgressBars" data-title="IPs provisioned per region"></div>
</li> </li>
<!-- <li data-row="3" data-col="2" data-sizex="2" data-sizey="1"> <li data-row="3" data-col="2" data-sizex="2" data-sizey="1">
<div data-id="convergence" data-view="Graph" data-title="Convergence" style="background-color:#ff9618"></div> <div data-id="api_response" data-view="Graph" data-title="Api Response Times" ></div>
</li> --> </li>
</ul> </ul>
</div> </div>

View File

@@ -1,5 +1,5 @@
<h1 class="title" data-bind="title"></h1> <h1 class="title" data-bind="title"></h1>
<h2 class="value" data-bind="current | prettyNumber | prepend prefix"></h2> <h2 class="value" data-bind="current | prepend prefix"></h2>
<p class="more-info" data-bind="moreinfo"></p> <p class="more-info" data-bind="moreinfo"></p>

View File

@@ -1,7 +1,7 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Sass declarations // Sass declarations
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
$background-color: #dc5945; $background-color: #1A773F;
$title-color: rgba(255, 255, 255, 0.7); $title-color: rgba(255, 255, 255, 0.7);
$moreinfo-color: rgba(255, 255, 255, 0.3); $moreinfo-color: rgba(255, 255, 255, 0.3);
@@ -16,11 +16,16 @@ $tick-color: rgba(0, 0, 0, 0.4);
background-color: $background-color; background-color: $background-color;
position: relative; position: relative;
h2{
font-size: 25px;
white-space: pre;
}
svg { svg {
position: absolute; position: absolute;
opacity: 0.4; opacity: 0.6;
fill-opacity: 0.4; fill-opacity: 0.6;
left: 0px; left: 0px;
top: 0px; top: 0px;
} }