From 305cee63d57ddeaa6cacdcca124d3e60bcc8cac4 Mon Sep 17 00:00:00 2001 From: adriant Date: Wed, 17 Dec 2014 14:49:28 +1300 Subject: [PATCH] graphing api response times Adding timing functionality around all api calls and outputting that to a graph on the dashboard itself. --- pydashie/assets/stylesheets/application.css | 53 +++++---- pydashie/openstack_samplers.py | 124 ++++++++++++-------- pydashie/templates/main.html | 6 +- pydashie/widgets/graph/graph.html | 2 +- pydashie/widgets/graph/graph.scss | 11 +- 5 files changed, 118 insertions(+), 78 deletions(-) diff --git a/pydashie/assets/stylesheets/application.css b/pydashie/assets/stylesheets/application.css index 8e566ea..0413688 100644 --- a/pydashie/assets/stylesheets/application.css +++ b/pydashie/assets/stylesheets/application.css @@ -167,32 +167,35 @@ color: rgba(255, 255, 255, 0.5); } .widget-graph { - background-color: #dc5945; + background-color: #1A773F; position: relative; } - .widget-graph svg { - position: absolute; - opacity: 0.4; - fill-opacity: 0.4; - left: 0px; - top: 0px; } - .widget-graph .title, .widget-graph .value { - position: relative; - z-index: 99; } - .widget-graph .title { - color: rgba(255, 255, 255, 0.7); } - .widget-graph .more-info { - color: rgba(255, 255, 255, 0.3); - font-weight: 600; - font-size: 20px; - margin-top: 0; } - .widget-graph .x_tick { - position: absolute; - bottom: 0; } - .widget-graph .x_tick .title { - font-size: 20px; - color: rgba(0, 0, 0, 0.4); - opacity: 0.5; - padding-bottom: 3px; } + .widget-graph h2 { + font-size: 25px; + white-space: pre; } + .widget-graph svg { + position: absolute; + opacity: 0.6; + fill-opacity: 0.6; + left: 0px; + top: 0px; } + .widget-graph .title, .widget-graph .value { + position: relative; + z-index: 99; } + .widget-graph .title { + color: rgba(255, 255, 255, 0.7); } + .widget-graph .more-info { + color: rgba(255, 255, 255, 0.3); + font-weight: 600; + font-size: 20px; + margin-top: 0; } + .widget-graph .x_tick { + position: absolute; + bottom: 0; } + .widget-graph .x_tick .title { + font-size: 20px; + color: rgba(0, 0, 0, 0.4); + opacity: 0.5; + padding-bottom: 3px; } .widget-graph .y_ticks { font-size: 20px; fill: rgba(0, 0, 0, 0.4); diff --git a/pydashie/openstack_samplers.py b/pydashie/openstack_samplers.py index 1e8078b..09eb5d9 100644 --- a/pydashie/openstack_samplers.py +++ b/pydashie/openstack_samplers.py @@ -1,4 +1,8 @@ import collections +import datetime +import json +from contextlib import contextmanager + import random import nagios from math import ceil @@ -16,6 +20,8 @@ class BaseOpenstackSampler(DashieSampler): def __init__(self, app, interval, conf=None, client_cache={}): self._os_clients = client_cache self._conf = conf + self._res_time_X = 0 + self._response_times = collections.deque() super(BaseOpenstackSampler, self).__init__(app, interval) def _convert(self, num): @@ -73,6 +79,46 @@ class BaseOpenstackSampler(DashieSampler): 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): def __init__(self, *args, **kwargs): @@ -88,8 +134,10 @@ class CPUSampler(BaseOpenstackSampler): for region in self._conf['allocation'].keys(): nova = self._client('compute', region) - stats = nova.hypervisors.statistics() - hypervisors = nova.hypervisors.list() + with self.timed(): + stats = nova.hypervisors.statistics() + with self.timed(): + hypervisors = nova.hypervisors.list() reserved = 0 for hypervisor in hypervisors: @@ -124,8 +172,10 @@ class RAMSampler(BaseOpenstackSampler): for region in self._conf['allocation'].keys(): nova = self._client('compute', region) - stats = nova.hypervisors.statistics() - hypervisors = nova.hypervisors.list() + with self.timed(): + stats = nova.hypervisors.statistics() + with self.timed(): + hypervisors = nova.hypervisors.list() reserved = 0 for hypervisor in hypervisors: @@ -170,8 +220,10 @@ class IPSampler(BaseOpenstackSampler): neutron = self._client('network', region) - ips = neutron.list_floatingips() - routers = neutron.list_routers() + with self.timed(): + ips = neutron.list_floatingips() + with self.timed(): + routers = neutron.list_routers() net_gateways = 0 for router in routers['routers']: @@ -202,8 +254,10 @@ class RegionsCPUSampler(BaseOpenstackSampler): for region in self._conf['allocation'].keys(): nova = self._client('compute', region) - stats = nova.hypervisors.statistics() - hypervisors = nova.hypervisors.list() + with self.timed(): + stats = nova.hypervisors.statistics() + with self.timed(): + hypervisors = nova.hypervisors.list() reserved = 0 for hypervisor in hypervisors: @@ -232,8 +286,10 @@ class RegionsRAMSampler(BaseOpenstackSampler): for region in self._conf['allocation'].keys(): nova = self._client('compute', region) - stats = nova.hypervisors.statistics() - hypervisors = nova.hypervisors.list() + with self.timed(): + stats = nova.hypervisors.statistics() + with self.timed(): + hypervisors = nova.hypervisors.list() reserved = 0 for hypervisor in hypervisors: @@ -269,8 +325,10 @@ class RegionIPSampler(BaseOpenstackSampler): for region in self._conf['allocation'].keys(): neutron = self._client('network', region) - ips = neutron.list_floatingips() - routers = neutron.list_routers() + with self.timed(): + ips = neutron.list_floatingips() + with self.timed(): + routers = neutron.list_routers() net_gateways = 0 for router in routers['routers']: @@ -368,18 +426,22 @@ class ResourceSampler(BaseOpenstackSampler): nova = self._client('compute', region) # cinder = self._client('storage', region) - stats = nova.hypervisors.statistics() + with self.timed(): + stats = nova.hypervisors.statistics() resources['instances'] = resources['instances'] + stats.running_vms - routers = neutron.list_routers() + with self.timed(): + routers = neutron.list_routers() resources['routers'] = (resources['routers'] + len(routers['routers'])) - networks = neutron.list_networks() + with self.timed(): + networks = neutron.list_networks() resources['networks'] = (resources['networks'] + len(networks['networks'])) - vpns = neutron.list_vpnservices() + with self.timed(): + vpns = neutron.list_vpnservices() resources['vpns'] = (resources['vpns'] + len(vpns['vpnservices'])) @@ -392,33 +454,3 @@ class ResourceSampler(BaseOpenstackSampler): items.append({'label': key, 'value': value}) 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} diff --git a/pydashie/templates/main.html b/pydashie/templates/main.html index c7292b7..b033532 100644 --- a/pydashie/templates/main.html +++ b/pydashie/templates/main.html @@ -60,9 +60,9 @@
- +
  • +
    +
  • diff --git a/pydashie/widgets/graph/graph.html b/pydashie/widgets/graph/graph.html index d1794db..ad14f8d 100644 --- a/pydashie/widgets/graph/graph.html +++ b/pydashie/widgets/graph/graph.html @@ -1,5 +1,5 @@

    -

    +

    \ No newline at end of file diff --git a/pydashie/widgets/graph/graph.scss b/pydashie/widgets/graph/graph.scss index 71cd4d4..7a30d12 100644 --- a/pydashie/widgets/graph/graph.scss +++ b/pydashie/widgets/graph/graph.scss @@ -1,7 +1,7 @@ // ---------------------------------------------------------------------------- // Sass declarations // ---------------------------------------------------------------------------- -$background-color: #dc5945; +$background-color: #1A773F; $title-color: rgba(255, 255, 255, 0.7); $moreinfo-color: rgba(255, 255, 255, 0.3); @@ -16,11 +16,16 @@ $tick-color: rgba(0, 0, 0, 0.4); background-color: $background-color; position: relative; + h2{ + font-size: 25px; + white-space: pre; + } + svg { position: absolute; - opacity: 0.4; - fill-opacity: 0.4; + opacity: 0.6; + fill-opacity: 0.6; left: 0px; top: 0px; }