From 7ed35dee84895f021196439605732b4db17b502d Mon Sep 17 00:00:00 2001 From: Ladislav Smola Date: Thu, 31 Jul 2014 16:11:52 +0200 Subject: [PATCH] Extracting common function into utils Extracting common metering functins into utils/metering so they can be reused in multiple places. Moving old utils file under utils directory. Change-Id: Id45b6237a68a894721e1b2051f64192781f02f8f --- tuskar_ui/api/heat.py | 2 +- tuskar_ui/api/node.py | 2 +- tuskar_ui/infrastructure/nodes/views.py | 230 +++--------------- tuskar_ui/infrastructure/overcloud/tabs.py | 2 +- .../plans/workflows/create_configuration.py | 2 +- tuskar_ui/utils/__init__.py | 0 tuskar_ui/utils/metering.py | 200 +++++++++++++++ tuskar_ui/{ => utils}/utils.py | 0 8 files changed, 232 insertions(+), 206 deletions(-) create mode 100644 tuskar_ui/utils/__init__.py create mode 100644 tuskar_ui/utils/metering.py rename tuskar_ui/{ => utils}/utils.py (100%) diff --git a/tuskar_ui/api/heat.py b/tuskar_ui/api/heat.py index f2dded8a3..abd726031 100644 --- a/tuskar_ui/api/heat.py +++ b/tuskar_ui/api/heat.py @@ -28,7 +28,7 @@ from tuskar_ui.api import tuskar from tuskar_ui.cached_property import cached_property # noqa from tuskar_ui.handle_errors import handle_errors # noqa from tuskar_ui.test.test_data import heat_data -from tuskar_ui import utils +from tuskar_ui.utils import utils TEST_DATA = test_utils.TestDataContainer() diff --git a/tuskar_ui/api/node.py b/tuskar_ui/api/node.py index fed6fe86c..cc16abe6a 100644 --- a/tuskar_ui/api/node.py +++ b/tuskar_ui/api/node.py @@ -24,7 +24,7 @@ from tuskar_ui.cached_property import cached_property # noqa from tuskar_ui.handle_errors import handle_errors # noqa from tuskar_ui.test.test_data import heat_data from tuskar_ui.test.test_data import node_data -from tuskar_ui import utils +from tuskar_ui.utils import utils TEST_DATA = test_utils.TestDataContainer() diff --git a/tuskar_ui/infrastructure/nodes/views.py b/tuskar_ui/infrastructure/nodes/views.py index 9d4aa1e08..b8ea03a09 100644 --- a/tuskar_ui/infrastructure/nodes/views.py +++ b/tuskar_ui/infrastructure/nodes/views.py @@ -11,16 +11,10 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. - -from datetime import datetime # noqa -from datetime import timedelta # noqa -from django.utils import timezone - import json from django.core.urlresolvers import reverse_lazy from django import http -from django.utils.http import urlencode from django.utils.translation import ugettext_lazy as _ from django.views.generic import base @@ -30,13 +24,11 @@ from horizon import tabs as horizon_tabs from horizon import views as horizon_views from openstack_dashboard.api import base as api_base -from openstack_dashboard.api import ceilometer - -from openstack_dashboard.dashboards.admin.metering import views as metering from tuskar_ui import api from tuskar_ui.infrastructure.nodes import forms from tuskar_ui.infrastructure.nodes import tabs +from tuskar_ui.utils import metering as metering_utils class IndexView(horizon_tabs.TabbedTableView): @@ -81,19 +73,20 @@ class DetailView(horizon_views.APIView): # (meter label, url part, barchart (True/False)) context['meter_conf'] = ( (_('System Load'), - url_part('hardware.cpu.load.1min', False), + metering_utils.url_part('hardware.cpu.load.1min', False), None), (_('CPU Utilization'), - url_part('hardware.system_stats.cpu.util', True), + metering_utils.url_part('hardware.system_stats.cpu.util', + True), '100'), (_('Swap Utilization'), - url_part('swap-util', True), + metering_utils.url_part('swap-util', True), '100'), (_('Disk I/O '), - url_part('disk-io', False), + metering_utils.url_part('disk-io', False), None), (_('Network I/O '), - url_part('network-io', False), + metering_utils.url_part('network-io', False), None), ) return context @@ -161,29 +154,30 @@ class PerformanceView(base.TemplateView): # Disk and Network I/O: data from 2 meters in one chart if meter == 'disk-io': - meters = get_meters([ + meters = metering_utils.get_meters([ 'hardware.system_stats.io.raw_sent', 'hardware.system_stats.io.raw_received' ]) elif meter == 'network-io': - meters = get_meters([ + meters = metering_utils.get_meters([ 'hardware.network.ip.out_requests', 'hardware.network.ip.in_receives' ]) elif meter == 'swap-util': - meters = get_meters([ + meters = metering_utils.get_meters([ 'hardware.memory.swap.util', 'hardware.memory.swap.total' ]) else: - meters = get_meters([meter]) + meters = metering_utils.get_meters([meter]) - date_from, date_to = _calc_date_args(date_from, - date_to, - date_options) + date_from, date_to = metering_utils._calc_date_args( + date_from, + date_to, + date_options) for meter_id, meter_name in meters: - resources, unit = query_data( + resources, unit = metering_utils.query_data( request=request, date_from=date_from, date_to=date_to, @@ -219,193 +213,25 @@ class PerformanceView(base.TemplateView): series = [util] if series and barchart: - average, used, tooltip_average = get_barchart_stats(series, - unit) + average, used, tooltip_average = ( + metering_utils.get_barchart_stats(series, unit)) + if date_from: start_datetime = date_from.strftime("%Y-%m-%dT%H:%M:%S") if date_to: end_datetime = date_to.strftime("%Y-%m-%dT%H:%M:%S") - json_output = create_json_output(series, start_datetime, end_datetime) + json_output = metering_utils.create_json_output( + series, + start_datetime, + end_datetime) if series and barchart: - json_output = add_barchart_settings(json_output, average, used, - tooltip_average) + json_output = metering_utils.add_barchart_settings( + json_output, + average, + used, + tooltip_average) return http.HttpResponse(json.dumps(json_output), mimetype='application/json') - - -#TODO(lsmola) this should probably live in Horizon common -def query_data(request, - date_from, - date_to, - group_by, - meter, - period=None, - query=None, - additional_query=None): - - if not period: - period = _calc_period(date_from, date_to, 50) - if additional_query is None: - additional_query = [] - if date_from: - additional_query += [{'field': 'timestamp', - 'op': 'ge', - 'value': date_from}] - if date_to: - additional_query += [{'field': 'timestamp', - 'op': 'le', - 'value': date_to}] - - # TODO(lsmola) replace this by logic implemented in I1 in bugs - # 1226479 and 1226482, this is just a quick fix for RC1 - try: - meter_list = [m for m in metering.meter_list(request) - if m.name == meter] - unit = meter_list[0].unit - except Exception: - unit = "" - if group_by == "resources": - # TODO(lsmola) need to implement group_by groups of resources - resources = [] - unit = "" - else: - ceilometer_usage = ceilometer.CeilometerUsage(request) - try: - resources = ceilometer_usage.resources_with_statistics( - query, [meter], period=period, stats_attr=None, - additional_query=additional_query) - except Exception: - resources = [] - exceptions.handle(request, - _('Unable to retrieve statistics.')) - return resources, unit - - -#TODO(lsmola) push this function to Horizon common then delete this -def _calc_period(date_from, date_to, number_of_samples=400): - if date_from and date_to: - if date_to < date_from: - # TODO(lsmola) propagate the Value error through Horizon - # handler to the client with verbose message. - raise ValueError("Date to must be bigger than date " - "from.") - # get the time delta in seconds - delta = date_to - date_from - delta_in_seconds = delta.days * 24 * 3600 + delta.seconds - - period = delta_in_seconds / number_of_samples - else: - # If some date is missing, just set static window to one day. - period = 3600 * 24 - return period - - -#TODO(lsmola) push this function to Horizon common then delete this -def _calc_date_args(date_from, date_to, date_options): - # TODO(lsmola) all timestamps should probably work with - # current timezone. And also show the current timezone in chart. - if date_options == "other": - try: - if date_from: - date_from = datetime.strptime(date_from, - "%Y-%m-%d") - else: - # TODO(lsmola) there should be probably the date - # of the first sample as default, so it correctly - # counts the time window. Though I need ordering - # and limit of samples to obtain that. - pass - if date_to: - date_to = datetime.strptime(date_to, - "%Y-%m-%d") - # It return beginning of the day, I want the and of - # the day, so i will add one day without a second. - date_to = (date_to + timedelta(days=1) - - timedelta(seconds=1)) - else: - date_to = timezone.now() - except Exception: - raise ValueError("The dates haven't been " - "recognized") - else: - try: - date_from = timezone.now() - timedelta(days=float(date_options)) - date_to = timezone.now() - except Exception: - raise ValueError("The time delta must be an " - "integer representing days.") - return date_from, date_to - - -def url_part(meter_name, barchart): - d = {'meter': meter_name} - if barchart: - d['barchart'] = True - return urlencode(d) - - -def get_meter_name(meter): - return meter.replace('.', '_') - - -def get_meters(meters): - return [(m, get_meter_name(m)) for m in meters] - - -def get_barchart_stats(series, unit): - values = [point['y'] for point in series[0]['data']] - average = sum(values) / len(values) - used = values[-1] - first_date = series[0]['data'][0]['x'] - last_date = series[0]['data'][-1]['x'] - tooltip_average = _('Average %(average)s %(unit)s
From: ' - '%(first_date)s, to: %(last_date)s') % ( - dict(average=average, unit=unit, - first_date=first_date, - last_date=last_date) - ) - return average, used, tooltip_average - - -def create_json_output(series, start_datetime, end_datetime): - return { - 'series': series, - 'settings': { - 'renderer': 'StaticAxes', - 'yMin': 0, - 'xMin': start_datetime, - 'xMax': end_datetime, - 'higlight_last_point': True, - 'auto_size': False, - 'auto_resize': False, - 'axes_x': False, - 'axes_y': True, - 'axes_y_label': False, - }, - } - - -def add_barchart_settings(ret, average, used, tooltip_average): - ret['settings']['bar_chart_settings'] = { - 'orientation': 'vertical', - 'used_label_placement': 'left', - 'width': 30, - 'color_scale_domain': [0, 80, 80, 100], - 'color_scale_range': [ - '#0000FF', - '#0000FF', - '#FF0000', - '#FF0000' - ], - 'average_color_scale_domain': [0, 100], - 'average_color_scale_range': ['#0000FF', '#0000FF'] - } - ret['stats'] = { - 'average': average, - 'used': used, - 'tooltip_average': tooltip_average, - } - return ret diff --git a/tuskar_ui/infrastructure/overcloud/tabs.py b/tuskar_ui/infrastructure/overcloud/tabs.py index a67a00345..0e4e580d5 100644 --- a/tuskar_ui/infrastructure/overcloud/tabs.py +++ b/tuskar_ui/infrastructure/overcloud/tabs.py @@ -17,7 +17,7 @@ import heatclient from horizon import tabs from tuskar_ui.infrastructure.overcloud import tables -from tuskar_ui import utils +from tuskar_ui.utils import utils def _get_role_data(stack, role): diff --git a/tuskar_ui/infrastructure/plans/workflows/create_configuration.py b/tuskar_ui/infrastructure/plans/workflows/create_configuration.py index 41a9f84de..13fd21207 100644 --- a/tuskar_ui/infrastructure/plans/workflows/create_configuration.py +++ b/tuskar_ui/infrastructure/plans/workflows/create_configuration.py @@ -16,7 +16,7 @@ from django.utils.translation import ugettext_lazy as _ import horizon.workflows from openstack_dashboard.api import neutron -from tuskar_ui import utils +from tuskar_ui.utils import utils def make_field(name, Type, NoEcho, Default, Description, AllowedValues=None, diff --git a/tuskar_ui/utils/__init__.py b/tuskar_ui/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tuskar_ui/utils/metering.py b/tuskar_ui/utils/metering.py new file mode 100644 index 000000000..dded8531d --- /dev/null +++ b/tuskar_ui/utils/metering.py @@ -0,0 +1,200 @@ +# -*- coding: utf8 -*- +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from datetime import datetime # noqa +from datetime import timedelta # noqa + +from django.utils.http import urlencode +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ + +from horizon import exceptions + +from openstack_dashboard.api import ceilometer +from openstack_dashboard.dashboards.admin.metering import views as metering + + +#TODO(lsmola) this should probably live in Horizon common +def query_data(request, + date_from, + date_to, + group_by, + meter, + period=None, + query=None, + additional_query=None): + + if not period: + period = _calc_period(date_from, date_to, 50) + if additional_query is None: + additional_query = [] + if date_from: + additional_query += [{'field': 'timestamp', + 'op': 'ge', + 'value': date_from}] + if date_to: + additional_query += [{'field': 'timestamp', + 'op': 'le', + 'value': date_to}] + + # TODO(lsmola) replace this by logic implemented in I1 in bugs + # 1226479 and 1226482, this is just a quick fix for RC1 + try: + meter_list = [m for m in metering.meter_list(request) + if m.name == meter] + unit = meter_list[0].unit + except Exception: + unit = "" + if group_by == "resources": + # TODO(lsmola) need to implement group_by groups of resources + resources = [] + unit = "" + else: + ceilometer_usage = ceilometer.CeilometerUsage(request) + try: + resources = ceilometer_usage.resources_with_statistics( + query, [meter], period=period, stats_attr=None, + additional_query=additional_query) + except Exception: + resources = [] + exceptions.handle(request, + _('Unable to retrieve statistics.')) + return resources, unit + + +#TODO(lsmola) push this function to Horizon common then delete this +def _calc_period(date_from, date_to, number_of_samples=400): + if date_from and date_to: + if date_to < date_from: + # TODO(lsmola) propagate the Value error through Horizon + # handler to the client with verbose message. + raise ValueError("Date to must be bigger than date " + "from.") + # get the time delta in seconds + delta = date_to - date_from + delta_in_seconds = delta.days * 24 * 3600 + delta.seconds + + period = delta_in_seconds / number_of_samples + else: + # If some date is missing, just set static window to one day. + period = 3600 * 24 + return period + + +#TODO(lsmola) push this function to Horizon common then delete this +def _calc_date_args(date_from, date_to, date_options): + # TODO(lsmola) all timestamps should probably work with + # current timezone. And also show the current timezone in chart. + if date_options == "other": + try: + if date_from: + date_from = datetime.strptime(date_from, + "%Y-%m-%d") + else: + # TODO(lsmola) there should be probably the date + # of the first sample as default, so it correctly + # counts the time window. Though I need ordering + # and limit of samples to obtain that. + pass + if date_to: + date_to = datetime.strptime(date_to, + "%Y-%m-%d") + # It return beginning of the day, I want the and of + # the day, so i will add one day without a second. + date_to = (date_to + timedelta(days=1) - + timedelta(seconds=1)) + else: + date_to = timezone.now() + except Exception: + raise ValueError("The dates haven't been " + "recognized") + else: + try: + date_from = timezone.now() - timedelta(days=float(date_options)) + date_to = timezone.now() + except Exception: + raise ValueError("The time delta must be an " + "integer representing days.") + return date_from, date_to + + +def url_part(meter_name, barchart): + d = {'meter': meter_name} + if barchart: + d['barchart'] = True + return urlencode(d) + + +def get_meter_name(meter): + return meter.replace('.', '_') + + +def get_meters(meters): + return [(m, get_meter_name(m)) for m in meters] + + +def get_barchart_stats(series, unit): + values = [point['y'] for point in series[0]['data']] + average = sum(values) / len(values) + used = values[-1] + first_date = series[0]['data'][0]['x'] + last_date = series[0]['data'][-1]['x'] + tooltip_average = _('Average %(average)s %(unit)s
From: ' + '%(first_date)s, to: %(last_date)s') % ( + dict(average=average, unit=unit, + first_date=first_date, + last_date=last_date) + ) + return average, used, tooltip_average + + +def create_json_output(series, start_datetime, end_datetime): + return { + 'series': series, + 'settings': { + 'renderer': 'StaticAxes', + 'yMin': 0, + 'xMin': start_datetime, + 'xMax': end_datetime, + 'higlight_last_point': True, + 'auto_size': False, + 'auto_resize': False, + 'axes_x': False, + 'axes_y': True, + 'axes_y_label': False, + }, + } + + +def add_barchart_settings(ret, average, used, tooltip_average): + ret['settings']['bar_chart_settings'] = { + 'orientation': 'vertical', + 'used_label_placement': 'left', + 'width': 30, + 'color_scale_domain': [0, 80, 80, 100], + 'color_scale_range': [ + '#0000FF', + '#0000FF', + '#FF0000', + '#FF0000' + ], + 'average_color_scale_domain': [0, 100], + 'average_color_scale_range': ['#0000FF', '#0000FF'] + } + ret['stats'] = { + 'average': average, + 'used': used, + 'tooltip_average': tooltip_average, + } + return ret diff --git a/tuskar_ui/utils.py b/tuskar_ui/utils/utils.py similarity index 100% rename from tuskar_ui/utils.py rename to tuskar_ui/utils/utils.py