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:
@@ -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);
|
||||||
|
|||||||
@@ -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}
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user