Improve CloudKitty's dashboard

Work items:

     - The "Project/Rating" tab has been improved: it does now provide a total
       by metric type. This make use of the /summary endpoint instead of /total
       (/total is deprecated).

     - An "Admin/Rating Summary" tab has been added. An admin user can now have
       the cost of every rated tenant at once. By clicking on a tenant, a
       per-resource total for the given tenant can be obtained (this view is
       similar to the "Project/Rating" tab). A per-resource total for the whole
       cloud is also available.

Change-Id: I13b9ba9e04a330ec216378258ad024c8651f6ff5
This commit is contained in:
Luka Peschke 2018-07-05 17:46:06 +02:00
parent 8b342a8372
commit b7304ab60c
13 changed files with 275 additions and 19 deletions

View File

@ -22,7 +22,6 @@ from horizon import tabs
from horizon import views
from keystoneauth1 import exceptions
# from cloudkittyclient.apiclient import exceptions
from cloudkittydashboard.api import cloudkitty as api
from cloudkittydashboard.dashboards.admin.hashmap import forms as hashmap_forms
from cloudkittydashboard.dashboards.admin.hashmap \

View File

@ -0,0 +1,22 @@
# Copyright 2018 Objectif Libre
#
# 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 django.utils.translation import ugettext_lazy as _
import horizon
class Summary(horizon.Panel):
name = _("Rating Summary")
slug = "rating_summary"

View File

@ -0,0 +1,45 @@
# Copyright 2018 Objectif Libre
#
# 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 django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import tables
def get_details_link(datum):
if datum.tenant_id:
url = "horizon:admin:rating_summary:project_details"
return reverse(url, kwargs={'project_id': datum.tenant_id})
class SummaryTable(tables.DataTable):
project_id = tables.Column(
'tenant_id', verbose_name=_("Project ID"), link=get_details_link)
project_name = tables.Column(
'name', verbose_name=_("Project Name"), link=get_details_link)
total = tables.Column('rate', verbose_name=_("Project Total"))
class Meta(object):
name = "summary"
verbose_name = _("Summary")
class TenantSummaryTable(tables.DataTable):
res_type = tables.Column('res_type', verbose_name=_("Res Type"))
rate = tables.Column('rate', verbose_name=_("Rate"))
class Meta(object):
name = "tenant_summary"
verbose_name = _("Tenant Summary")

View File

@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Tenant Details" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Tenant Details") %}
{% endblock page_header %}
{% block main %}
{% trans "Project ID:" %} {{ project_id }}
{{ table.render }}
{{ modules }}
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Summary" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Summary") %}
{% endblock page_header %}
{% block main %}
{{ table.render }}
{{ modules }}
{% endblock %}

View File

@ -0,0 +1,23 @@
# Copyright 2018 Objectif Libre
#
# 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 django.conf.urls import url
from cloudkittydashboard.dashboards.admin.summary import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<project_id>[^/]+)/?$', views.TenantDetailsView.as_view(),
name="project_details"),
]

View File

@ -0,0 +1,68 @@
# Copyright 2018 Objectif Libre
#
# 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 django.utils.translation import ugettext_lazy as _
from horizon import tables
from openstack_dashboard.api import keystone as api_keystone
from cloudkittydashboard.api import cloudkitty as api
from cloudkittydashboard.dashboards.admin.summary import tables as sum_tables
class IndexView(tables.DataTableView):
template_name = 'admin/rating_summary/index.html'
table_class = sum_tables.SummaryTable
def get_data(self):
summary = api.cloudkittyclient(self.request).report.get_summary(
groupby=['tenant_id'], all_tenants=True)['summary']
tenants, _ = api_keystone.tenant_list(self.request)
tenants = {tenant.id: tenant.name for tenant in tenants}
summary.append({
'tenant_id': 'ALL',
'rate': sum([float(item['rate']) for item in summary]),
})
summary = api.identify(summary, key='tenant_id')
for tenant in summary:
tenant['name'] = tenants.get(tenant.id, '-')
summary[-1]['name'] = 'Cloud Total'
return summary
class TenantDetailsView(tables.DataTableView):
template_name = 'admin/rating_summary/details.html'
table_class = sum_tables.TenantSummaryTable
page_title = _("Script Details : {{ table.project_id }}")
def _get_cloud_total_summary(self):
return api.cloudkittyclient(self.request).report.get_summary(
groupby=['res_type'], all_tenants=True)['summary']
def get_data(self):
tenant_id = self.kwargs['project_id']
if tenant_id == 'ALL':
summary = self._get_cloud_total_summary()
else:
summary = api.cloudkittyclient(self.request).report.get_summary(
groupby=['res_type'], tenant_id=tenant_id)['summary']
summary.append({
'tenant_id': tenant_id,
'res_type': 'TOTAL',
'rate': sum([float(item['rate']) for item in summary]),
})
summary = api.identify(summary, key='res_type', name=True)
return summary

View File

@ -0,0 +1,27 @@
# Copyright 2018 Objectif Libre
#
# 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 django.utils.translation import ugettext_lazy as _
from horizon import tables
class SummaryTable(tables.DataTable):
"""This table formats a summary for the given tenant."""
res_type = tables.Column('res_type', verbose_name=_('Metric Type'))
rate = tables.Column('rate', verbose_name=_('Rate'))
class Meta(object):
name = "summary"
verbose_name = _("Summary")

View File

@ -7,11 +7,8 @@
{% endblock page_header %}
{% block main %}
<hr class="header_rule"></hr>
<div class="status row-fluid detail">
<dl>
<dt> {% trans "Total" %}</dt>
<dd> {{ total }}</dd>
</dl>
</div>
{{ table.render }}
{{ modules }}
{% endblock %}

View File

@ -18,21 +18,30 @@ import json
from django import http
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import views
from horizon import tables
from cloudkittydashboard.api import cloudkitty as api
from cloudkittydashboard.dashboards.project.rating \
import tables as rating_tables
from cloudkittydashboard.utils import TemplatizableDict
class IndexView(views.APIView):
# A very simple class-based view...
class IndexView(tables.DataTableView):
table_class = rating_tables.SummaryTable
template_name = 'project/rating/index.html'
def get_data(self, request, context, *args, **kwargs):
# Add data to the context here...
total = api.cloudkittyclient(request).report.get_total(
tenant_id=request.user.tenant_id) or 0.00
context['total'] = total
return context
def get_data(self):
summary = api.cloudkittyclient(self.request).report.get_summary(
tenant_id=self.request.user.tenant_id,
groupby=['tenant_id', 'res_type'])['summary']
summary = api.identify(summary, key='res_type', name=True)
summary.append(TemplatizableDict({
'id': 'ALL',
'res_type': 'TOTAL',
'name': 'ALL',
'rate': sum([float(i['rate']) for i in summary]),
}))
return summary
def quote(request):
@ -42,8 +51,7 @@ def quote(request):
json_data = json.loads(request.body)
try:
pricing = decimal.Decimal(api.cloudkittyclient(request)
.quotations.quote(json_data))
pricing = pricing.normalize().to_eng_string()
.rating.get_quotation(json_data))
except Exception:
exceptions.handle(request,
_('Unable to retrieve price.'))

View File

@ -0,0 +1,21 @@
# Copyright 2018 Objectif Libre
#
# 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.
PANEL_GROUP = 'rating'
PANEL_DASHBOARD = 'admin'
PANEL = 'rating_summary'
# Python panel class of the PANEL to be added.
ADD_PANEL = \
'cloudkittydashboard.dashboards.admin.summary.panel.Summary'

View File

@ -0,0 +1,14 @@
---
features:
- |
An "Admin/Rating Summary" tab has been added. An admin user can now have
the cost of every rated tenant at once. By clicking on a tenant, a
per-resource total for the given tenant can be obtained (this view is
similar to the "Project/Rating" tab). A per-resource total for the whole
cloud is also available.
upgrade:
- |
The "Project/Rating" tab has been improved: it does now provide a total
by metric type. This make use of the /summary endpoint instead of /total
(/total is deprecated).