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:
parent
8b342a8372
commit
b7304ab60c
|
@ -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 \
|
||||
|
|
|
@ -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"
|
|
@ -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")
|
|
@ -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 %}
|
|
@ -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 %}
|
||||
|
||||
|
|
@ -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"),
|
||||
]
|
|
@ -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
|
|
@ -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")
|
|
@ -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 %}
|
||||
|
|
|
@ -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.'))
|
||||
|
|
|
@ -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'
|
|
@ -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).
|
Loading…
Reference in New Issue