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 horizon import views
|
||||||
from keystoneauth1 import exceptions
|
from keystoneauth1 import exceptions
|
||||||
|
|
||||||
# from cloudkittyclient.apiclient import exceptions
|
|
||||||
from cloudkittydashboard.api import cloudkitty as api
|
from cloudkittydashboard.api import cloudkitty as api
|
||||||
from cloudkittydashboard.dashboards.admin.hashmap import forms as hashmap_forms
|
from cloudkittydashboard.dashboards.admin.hashmap import forms as hashmap_forms
|
||||||
from cloudkittydashboard.dashboards.admin.hashmap \
|
from cloudkittydashboard.dashboards.admin.hashmap \
|
||||||
|
22
cloudkittydashboard/dashboards/admin/summary/panel.py
Normal file
22
cloudkittydashboard/dashboards/admin/summary/panel.py
Normal 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"
|
45
cloudkittydashboard/dashboards/admin/summary/tables.py
Normal file
45
cloudkittydashboard/dashboards/admin/summary/tables.py
Normal 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")
|
@ -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 %}
|
||||||
|
|
||||||
|
|
23
cloudkittydashboard/dashboards/admin/summary/urls.py
Normal file
23
cloudkittydashboard/dashboards/admin/summary/urls.py
Normal 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"),
|
||||||
|
]
|
68
cloudkittydashboard/dashboards/admin/summary/views.py
Normal file
68
cloudkittydashboard/dashboards/admin/summary/views.py
Normal 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
|
27
cloudkittydashboard/dashboards/project/rating/tables.py
Normal file
27
cloudkittydashboard/dashboards/project/rating/tables.py
Normal 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")
|
@ -7,11 +7,8 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<hr class="header_rule"></hr>
|
|
||||||
<div class="status row-fluid detail">
|
{{ table.render }}
|
||||||
<dl>
|
|
||||||
<dt> {% trans "Total" %}</dt>
|
{{ modules }}
|
||||||
<dd> {{ total }}</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -18,21 +18,30 @@ import json
|
|||||||
from django import http
|
from django import http
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import views
|
from horizon import tables
|
||||||
|
|
||||||
from cloudkittydashboard.api import cloudkitty as api
|
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):
|
class IndexView(tables.DataTableView):
|
||||||
# A very simple class-based view...
|
table_class = rating_tables.SummaryTable
|
||||||
template_name = 'project/rating/index.html'
|
template_name = 'project/rating/index.html'
|
||||||
|
|
||||||
def get_data(self, request, context, *args, **kwargs):
|
def get_data(self):
|
||||||
# Add data to the context here...
|
summary = api.cloudkittyclient(self.request).report.get_summary(
|
||||||
total = api.cloudkittyclient(request).report.get_total(
|
tenant_id=self.request.user.tenant_id,
|
||||||
tenant_id=request.user.tenant_id) or 0.00
|
groupby=['tenant_id', 'res_type'])['summary']
|
||||||
context['total'] = total
|
summary = api.identify(summary, key='res_type', name=True)
|
||||||
return context
|
summary.append(TemplatizableDict({
|
||||||
|
'id': 'ALL',
|
||||||
|
'res_type': 'TOTAL',
|
||||||
|
'name': 'ALL',
|
||||||
|
'rate': sum([float(i['rate']) for i in summary]),
|
||||||
|
}))
|
||||||
|
return summary
|
||||||
|
|
||||||
|
|
||||||
def quote(request):
|
def quote(request):
|
||||||
@ -42,8 +51,7 @@ def quote(request):
|
|||||||
json_data = json.loads(request.body)
|
json_data = json.loads(request.body)
|
||||||
try:
|
try:
|
||||||
pricing = decimal.Decimal(api.cloudkittyclient(request)
|
pricing = decimal.Decimal(api.cloudkittyclient(request)
|
||||||
.quotations.quote(json_data))
|
.rating.get_quotation(json_data))
|
||||||
pricing = pricing.normalize().to_eng_string()
|
|
||||||
except Exception:
|
except Exception:
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_('Unable to retrieve price.'))
|
_('Unable to retrieve price.'))
|
||||||
|
21
cloudkittydashboard/enabled/_11_admin_summary_panel.py
Normal file
21
cloudkittydashboard/enabled/_11_admin_summary_panel.py
Normal 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'
|
14
releasenotes/notes/improve-total-tab-96df42d1e562fc4f.yaml
Normal file
14
releasenotes/notes/improve-total-tab-96df42d1e562fc4f.yaml
Normal 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).
|
Loading…
Reference in New Issue
Block a user