diff --git a/horizon/horizon/api/base.py b/horizon/horizon/api/base.py
index 6e88691d8..f53d06db1 100644
--- a/horizon/horizon/api/base.py
+++ b/horizon/horizon/api/base.py
@@ -61,7 +61,7 @@ class APIDictWrapper(object):
dictionary, in addition to attribute accesses.
Attribute access is the preferred method of access, to be
- consistent with api resource objects from openstackx
+ consistent with api resource objects from novclient.
"""
def __init__(self, apidict):
self._apidict = apidict
diff --git a/horizon/horizon/api/deprecated.py b/horizon/horizon/api/deprecated.py
deleted file mode 100644
index 74c0855b6..000000000
--- a/horizon/horizon/api/deprecated.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# All Rights Reserved.
-#
-# Copyright 2011 Nebula, Inc.
-#
-# 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.
-
-import functools
-import logging
-
-from django.utils.decorators import available_attrs
-import openstackx.admin
-import openstackx.api.exceptions as openstackx_exceptions
-import openstackx.extras
-
-from horizon.api.base import *
-
-
-LOG = logging.getLogger(__name__)
-
-
-def check_openstackx(f):
- """Decorator that adds extra info to api exceptions
-
- The OpenStack Dashboard currently depends on openstackx extensions
- being present in Nova. Error messages depending for views depending
- on these extensions do not lead to the conclusion that Nova is missing
- extensions.
-
- This decorator should be dropped and removed after Keystone and
- Horizon more gracefully handle extensions and openstackx extensions
- aren't required by Horizon in Nova.
- """
- @functools.wraps(f, assigned=available_attrs(f))
- def inner(*args, **kwargs):
- try:
- return f(*args, **kwargs)
- except openstackx_exceptions.NotFound, e:
- e.message = e.details or ''
- e.message += ' This error may be caused by a misconfigured' \
- ' Nova url in keystone\'s service catalog, or ' \
- ' by missing openstackx extensions in Nova. ' \
- ' See the Horizon README.'
- raise
- return inner
-
-
-def extras_api(request):
- management_url = url_for(request, 'compute')
- LOG.debug('extras_api connection created using token "%s"'
- ' and url "%s"' %
- (request.user.token, management_url))
- return openstackx.extras.Extras(auth_token=request.user.token,
- management_url=management_url)
diff --git a/horizon/horizon/api/nova.py b/horizon/horizon/api/nova.py
index 068c18ee2..f1b7d4719 100644
--- a/horizon/horizon/api/nova.py
+++ b/horizon/horizon/api/nova.py
@@ -28,8 +28,6 @@ from novaclient.v1_1 import security_group_rules as nova_rules
from novaclient.v1_1.servers import REBOOT_HARD
from horizon.api.base import *
-from horizon.api.deprecated import check_openstackx
-from horizon.api.deprecated import extras_api
LOG = logging.getLogger(__name__)
@@ -41,7 +39,7 @@ VOLUME_STATE_AVAILABLE = "available"
class Flavor(APIResourceWrapper):
- """Simple wrapper around openstackx.admin.flavors.Flavor"""
+ """Simple wrapper around novaclient.flavors.Flavor"""
_attrs = ['disk', 'id', 'links', 'name', 'ram', 'vcpus']
@@ -56,7 +54,7 @@ class FloatingIpPool(APIResourceWrapper):
class KeyPair(APIResourceWrapper):
- """Simple wrapper around openstackx.extras.keypairs.Keypair"""
+ """Simple wrapper around novaclient.keypairs.Keypair"""
_attrs = ['fingerprint', 'name', 'private_key']
@@ -99,7 +97,7 @@ class QuotaSet(object):
class Server(APIResourceWrapper):
- """Simple wrapper around openstackx.extras.server.Server
+ """Simple wrapper around novaclient.server.Server
Preserves the request info so image name can later be retrieved
"""
@@ -127,15 +125,33 @@ class Server(APIResourceWrapper):
class Usage(APIResourceWrapper):
- """Simple wrapper around openstackx.extras.usage.Usage"""
- _attrs = ['begin', 'instances', 'stop', 'tenant_id',
- 'total_active_disk_size', 'total_active_instances',
- 'total_active_ram_size', 'total_active_vcpus', 'total_cpu_usage',
- 'total_disk_usage', 'total_hours', 'total_ram_usage']
+ """Simple wrapper around contrib/simple_usage.py"""
+ _attrs = ['start', 'server_usages', 'stop', 'tenant_id',
+ 'total_local_gb_usage', 'total_memory_mb_usage',
+ 'total_vcpus_usage', 'total_hours']
+
+ @property
+ def total_active_instances(self):
+ return sum(1 for s in self.server_usages if s['ended_at'] == None)
+
+ @property
+ def total_active_vcpus(self):
+ return sum(s['vcpus']\
+ for s in self.server_usages if s['ended_at'] == None)
+
+ @property
+ def total_active_local_gb(self):
+ return sum(s['local_gb']\
+ for s in self.server_usages if s['ended_at'] == None)
+
+ @property
+ def total_active_memory_mb(self):
+ return sum(s['memory_mb']\
+ for s in self.server_usages if s['ended_at'] == None)
class SecurityGroup(APIResourceWrapper):
- """Simple wrapper around openstackx.extras.security_groups.SecurityGroup"""
+ """Simple wrapper around novaclient.security_groups.SecurityGroup"""
_attrs = ['id', 'name', 'description', 'tenant_id']
@property
@@ -285,7 +301,6 @@ def server_console_output(request, instance_id, tail_length=None):
length=tail_length)
-@check_openstackx
def admin_server_list(request):
return [Server(s, request) for s in novaclient(request).servers.list()]
@@ -349,14 +364,12 @@ def tenant_quota_defaults(request, tenant_id):
return QuotaSet(novaclient(request).quotas.defaults(tenant_id))
-@check_openstackx
def usage_get(request, tenant_id, start, end):
- return Usage(extras_api(request).usage.get(tenant_id, start, end))
+ return Usage(novaclient(request).usage.get(tenant_id, start, end))
-@check_openstackx
def usage_list(request, start, end):
- return [Usage(u) for u in extras_api(request).usage.list(start, end)]
+ return [Usage(u) for u in novaclient(request).usage.list(start, end, True)]
def security_group_list(request):
diff --git a/horizon/horizon/dashboards/nova/access_and_security/security_groups/tests.py b/horizon/horizon/dashboards/nova/access_and_security/security_groups/tests.py
index d7b786700..71d48b375 100644
--- a/horizon/horizon/dashboards/nova/access_and_security/security_groups/tests.py
+++ b/horizon/horizon/dashboards/nova/access_and_security/security_groups/tests.py
@@ -22,7 +22,6 @@ from django import http
from django.conf import settings
from django.core.urlresolvers import reverse
from glance.common import exception as glance_exception
-from openstackx.api import exceptions as api_exceptions
from novaclient import exceptions as novaclient_exceptions
from novaclient.v1_1 import security_group_rules as nova_rules
from mox import IgnoreArg, IsA
diff --git a/horizon/horizon/dashboards/nova/images_and_snapshots/snapshots/tests.py b/horizon/horizon/dashboards/nova/images_and_snapshots/snapshots/tests.py
index c53a338dd..69f71e45a 100644
--- a/horizon/horizon/dashboards/nova/images_and_snapshots/snapshots/tests.py
+++ b/horizon/horizon/dashboards/nova/images_and_snapshots/snapshots/tests.py
@@ -22,7 +22,7 @@ from django import http
from django.contrib import messages
from django.core.urlresolvers import reverse
from glance.common import exception as glance_exception
-from openstackx.api import exceptions as api_exceptions
+from novaclient import exceptions as novaclient_exceptions
from mox import IgnoreArg, IsA
from horizon import api
@@ -98,7 +98,7 @@ class SnapshotsViewTests(test.BaseViewTests):
def test_create_get_server_exception(self):
self.mox.StubOutWithMock(api, 'server_get')
- exception = api_exceptions.ApiException('apiException')
+ exception = novaclient_exceptions.ClientException('apiException')
api.server_get(IsA(http.HttpRequest),
str(self.good_server.id)).AndRaise(exception)
@@ -158,7 +158,7 @@ class SnapshotsViewTests(test.BaseViewTests):
api.server_get(IsA(http.HttpRequest),
str(self.good_server.id)).AndReturn(self.good_server)
- exception = api_exceptions.ApiException('apiException',
+ exception = novaclient_exceptions.ClientException('apiException',
message='apiException')
api.snapshot_create(IsA(http.HttpRequest),
str(self.good_server.id), SNAPSHOT_NAME).\
diff --git a/horizon/horizon/dashboards/nova/images_and_snapshots/views.py b/horizon/horizon/dashboards/nova/images_and_snapshots/views.py
index bcbb39772..f5814b402 100644
--- a/horizon/horizon/dashboards/nova/images_and_snapshots/views.py
+++ b/horizon/horizon/dashboards/nova/images_and_snapshots/views.py
@@ -30,7 +30,6 @@ from django.contrib import messages
from django.utils.translation import ugettext as _
from glance.common import exception as glance_exception
from novaclient import exceptions as novaclient_exceptions
-from openstackx.api import exceptions as api_exceptions
from horizon import api
from horizon import exceptions
diff --git a/horizon/horizon/dashboards/nova/instances_and_volumes/tests.py b/horizon/horizon/dashboards/nova/instances_and_volumes/tests.py
index 06fcd38e5..c8c7b5ad5 100644
--- a/horizon/horizon/dashboards/nova/instances_and_volumes/tests.py
+++ b/horizon/horizon/dashboards/nova/instances_and_volumes/tests.py
@@ -21,7 +21,7 @@
from django import http
from django.core.urlresolvers import reverse
from mox import IsA
-from openstackx.api import exceptions as api_exceptions
+from novaclient import exceptions as novaclient_exceptions
from horizon import api
from horizon import test
@@ -62,7 +62,7 @@ class InstancesAndVolumesViewTest(test.BaseViewTests):
def test_index_server_list_exception(self):
self.mox.StubOutWithMock(api, 'server_list')
self.mox.StubOutWithMock(api, 'volume_list')
- exception = api_exceptions.ApiException('apiException')
+ exception = novaclient_exceptions.ClientException('apiException')
api.server_list(IsA(http.HttpRequest)).AndRaise(exception)
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes)
diff --git a/horizon/horizon/dashboards/nova/overview/views.py b/horizon/horizon/dashboards/nova/overview/views.py
index 47f165f4f..9648e1973 100644
--- a/horizon/horizon/dashboards/nova/overview/views.py
+++ b/horizon/horizon/dashboards/nova/overview/views.py
@@ -58,24 +58,25 @@ def usage(request, tenant_id=None):
exceptions.handle(request,
_('Unable to retrieve usage information.'))
+ total_ram = 0
ram_unit = "MB"
- total_ram = getattr(usage, 'total_active_ram_size', 0)
- if total_ram >= 1024:
- ram_unit = "GB"
- total_ram /= 1024
instances = []
terminated = []
-
- if hasattr(usage, 'instances'):
+ if hasattr(usage, 'server_usages'):
+ total_ram = usage.total_active_memory_mb
now = datetime.datetime.now()
- for i in usage.instances:
+ for i in usage.server_usages:
i['uptime_at'] = now - datetime.timedelta(seconds=i['uptime'])
if i['ended_at'] and not show_terminated:
terminated.append(i)
else:
instances.append(i)
+ if total_ram >= 1024:
+ ram_unit = "GB"
+ total_ram /= 1024
+
if request.GET.get('format', 'html') == 'csv':
template = 'nova/overview/usage.csv'
mimetype = "text/csv"
diff --git a/horizon/horizon/dashboards/nova/templates/nova/overview/usage.csv b/horizon/horizon/dashboards/nova/templates/nova/overview/usage.csv
index d618b6370..450f9e022 100644
--- a/horizon/horizon/dashboards/nova/templates/nova/overview/usage.csv
+++ b/horizon/horizon/dashboards/nova/templates/nova/overview/usage.csv
@@ -1,11 +1,11 @@
Usage Report For Period:,{{datetime_start|date:"b. d Y H:i"}},/,{{datetime_end|date:"b. d Y H:i"}}
Tenant ID:,{{usage.tenant_id}}
Total Active VCPUs:,{{usage.total_active_vcpus}}
-CPU-HRs Used:,{{usage.total_cpu_usage}}
-Total Active Ram (MB):,{{usage.total_active_ram_size}}
-Total Disk Size:,{{usage.total_active_disk_size}}
-Total Disk Usage:,{{usage.total_disk_usage}}
+CPU-HRs Used:,{{usage.total_vcpus_usage}}
+Total Active Ram (MB):,{{usage.total_active_memory_mb}}
+Total Disk Size:,{{usage.total_active_local_gb}}
+Total Disk Usage:,{{usage.total_local_gb_usage}}
-ID,Name,UserId,VCPUs,RamMB,DiskGB,Flavor,Usage(Hours),Uptime(Seconds),State
-{% for instance in usage.instances %}{{instance.id}},{{instance.name|addslashes}},{{instance.user_id|addslashes}},{{instance.vcpus|addslashes}},{{instance.ram_size|addslashes}},{{instance.disk_size|addslashes}},{{instance.flavor|addslashes}},{{instance.hours}},{{instance.uptime}},{{instance.state|capfirst|addslashes}}
+ID,Name,VCPUs,RamMB,DiskGB,Usage(Hours),Uptime(Seconds),State
+{% for server_usage in usage.server_usages %}{{server_usage.id}},{{server_usage.name|addslashes}},{{server_usage.vcpus|addslashes}},{{server_usage.memory_mb|addslashes}},{{server_usage.local_gb|addslashes}},{{server_usage.hours}},{{server_usage.uptime}},{{server_usage.state|capfirst|addslashes}}
{% endfor %}
diff --git a/horizon/horizon/dashboards/nova/templates/nova/overview/usage.html b/horizon/horizon/dashboards/nova/templates/nova/overview/usage.html
index 7ff9bbaac..2f0fb73e1 100644
--- a/horizon/horizon/dashboards/nova/templates/nova/overview/usage.html
+++ b/horizon/horizon/dashboards/nova/templates/nova/overview/usage.html
@@ -8,13 +8,13 @@
{% block dash_main %}
- {% if usage.instances %}
+ {% if usage.server_usages %}
CPU
- {{ usage.total_active_vcpus|default:0 }}Cores Active
- - {{ usage.total_cpu_usage|floatformat|default:0 }}CPU-HR Used
+ - {{ usage.total_vcpus_usage|floatformat|default:0 }}CPU-HR Used
@@ -28,8 +28,8 @@
Disk
- - {{ usage.total_active_disk_size|default:0 }}GB Active
- - {{ usage.total_disk_usage|floatformat|default:0 }}GB-HR Used
+ - {{ usage.total_active_local_gb|default:0 }}GB Active
+ - {{ usage.total_local_gb|floatformat|default:0 }}GB-HR Used
@@ -49,7 +49,6 @@
{% trans "Name" %} |
- {% trans "User" %} |
{% trans "Size" %} |
{% trans "Uptime" %} |
{% trans "State" %} |
@@ -62,8 +61,7 @@
{% endif %}
{{ instance.name }} |
- {{ instance.user_id }} |
- {{ instance.ram_size|mbformat }} Ram | {{ instance.vcpus }} VCPU | {{ instance.disk_size }}GB Disk |
+ {{ instance.memory_mb|mbformat }} Ram | {{ instance.vcpus }} VCPU | {{ instance.local_gb }}GB Disk |
{{ instance.uptime_at|timesince }} |
{{ instance.state|lower|capfirst }} |
diff --git a/horizon/horizon/dashboards/syspanel/flavors/forms.py b/horizon/horizon/dashboards/syspanel/flavors/forms.py
index 490e08d8e..1d8f1117f 100644
--- a/horizon/horizon/dashboards/syspanel/flavors/forms.py
+++ b/horizon/horizon/dashboards/syspanel/flavors/forms.py
@@ -32,7 +32,7 @@ LOG = logging.getLogger(__name__)
class CreateFlavor(forms.SelfHandlingForm):
- #flavorid is required because of openstackx
+ # flavorid is required in novaclient
flavor_id = forms.IntegerField(label=_("Flavor ID"))
name = forms.CharField(max_length="25", label=_("Name"))
vcpus = forms.CharField(max_length="5", label=_("VCPUs"))
diff --git a/horizon/horizon/dashboards/syspanel/overview/views.py b/horizon/horizon/dashboards/syspanel/overview/views.py
index 04403cc3c..d7b513337 100644
--- a/horizon/horizon/dashboards/syspanel/overview/views.py
+++ b/horizon/horizon/dashboards/syspanel/overview/views.py
@@ -36,15 +36,8 @@ LOG = logging.getLogger(__name__)
class GlobalSummary(object):
- node_resources = ['vcpus', 'disk_size', 'ram_size']
- unit_mem_size = {'disk_size': ['GB', 'TB'], 'ram_size': ['MB', 'GB']}
- node_resource_info = ['', 'active_', 'avail_']
-
def __init__(self, request):
self.summary = {}
- for rsrc in GlobalSummary.node_resources:
- for info in GlobalSummary.node_resource_info:
- self.summary['total_' + info + rsrc] = 0
self.request = request
self.usage_list = []
@@ -57,24 +50,16 @@ class GlobalSummary(object):
_('Unable to retrieve usage information on date'
'range %(start)s to %(end)s' % {"start": start,
"end": end}))
- for usage in self.usage_list:
- for key in usage._attrs:
- val = getattr(usage, key)
- if isinstance(val, (float, int)):
- self.summary.setdefault(key, 0)
- self.summary[key] += val
- def human_readable(self, rsrc):
- if self.summary['total_' + rsrc] > 1023:
- self.summary['unit_' + rsrc] = GlobalSummary.unit_mem_size[rsrc][1]
- mult = 1024.0
- else:
- self.summary['unit_' + rsrc] = GlobalSummary.unit_mem_size[rsrc][0]
- mult = 1.0
+ # List of attrs on the Usage object that we would like to summarize
+ attrs = ['total_local_gb_usage', 'total_memory_mb_usage',
+ 'total_active_memory_mb', 'total_vcpus_usage',
+ 'total_active_instances']
- for kind in GlobalSummary.node_resource_info:
- self.summary['total_' + kind + rsrc + '_hr'] = \
- self.summary['total_' + kind + rsrc] / mult
+ for attr in attrs:
+ for usage in self.usage_list:
+ self.summary.setdefault(attr, 0)
+ self.summary[attr] += getattr(usage, attr)
@staticmethod
def next_month(date_start):
@@ -126,7 +111,7 @@ def usage(request):
template = 'syspanel/tenants/usage.csv'
mimetype = "text/csv"
else:
- template = 'syspanel/tenants/usage.html'
+ template = 'syspanel/tenants/global_usage.html'
mimetype = "text/html"
context = {'dateform': dateform,
diff --git a/horizon/horizon/dashboards/syspanel/services/views.py b/horizon/horizon/dashboards/syspanel/services/views.py
index 2d642d705..13dbf6922 100644
--- a/horizon/horizon/dashboards/syspanel/services/views.py
+++ b/horizon/horizon/dashboards/syspanel/services/views.py
@@ -26,7 +26,6 @@ import urlparse
from django import shortcuts
from django.contrib import messages
from django.utils.translation import ugettext as _
-from openstackx.api import exceptions as api_exceptions
from horizon import api
from horizon import tables
diff --git a/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/base_usage.html b/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/base_usage.html
new file mode 100644
index 000000000..78007e5f0
--- /dev/null
+++ b/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/base_usage.html
@@ -0,0 +1,40 @@
+{% extends 'syspanel/base.html' %}
+{% load i18n sizeformat %}
+{% block title %}Usage Overview{% endblock %}
+
+{% block page_header %}
+ {# to make searchable false, just remove it from the include statement #}
+ {% include "horizon/common/_page_header.html" with title=_("System Panel Overview") %}
+{% endblock page_header %}
+
+{% block syspanel_main %}
+{% if external_links %}
+
+
{% trans "Monitoring" %}:
+
+ {% for link in external_links %}
+ - {{ link.0}}
+ {% endfor %}
+
+
+{% endif %}
+
+
+
+
+ {% trans "Active Instances" %}: {{ global_summary.total_active_instances|default:'-' }}
+ {% trans "Active Memory" %}: {{ global_summary.total_active_memory_mb|mbformat|default:'-' }}
+ {% trans "This month's VCPU-Hours" %}: {{ global_summary.total_vcpus_usage|floatformat|default:'-' }}
+ {% trans "This month's GB-Hours" %}: {{ global_summary.total_local_gb_usage|floatformat|default:'-' }}
+
+
+{% block activity_list %}{% endblock %}
+
+{% endblock %}
diff --git a/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/global_usage.html b/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/global_usage.html
new file mode 100644
index 000000000..9c7b0dd7f
--- /dev/null
+++ b/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/global_usage.html
@@ -0,0 +1,43 @@
+{% extends 'syspanel/tenants/base_usage.html' %}
+{% load i18n sizeformat %}
+{% block title %}Usage Overview{% endblock %}
+{% block activity_list %}
+ {% if usage_list %}
+
+
+
+
+ {% trans "Tenant" %} |
+ {% trans "Instances" %} |
+ {% trans "VCPUs" %} |
+ {% trans "Disk" %} |
+ {% trans "RAM" %} |
+ {% trans "VCPU CPU-Hours" %} |
+ {% trans "Disk GB-Hours" %} |
+
+
+
+ {% for usage in usage_list %}
+
+ {{ usage.tenant_id }} |
+ {{ usage.total_active_instances }} |
+ {{ usage.total_active_vcpus }} |
+ {{ usage.total_active_local_gb|diskgbformat }} |
+ {{ usage.total_active_memory_mb|mbformat }} |
+ {{ usage.total_vcpus_usage|floatformat }} |
+ {{ usage.total_local_gb_usage|floatformat }} |
+
+ {% endfor %}
+
+
+
+
+ Server Usage Summary
+ {% trans "Download CSV" %} »
+ |
+
+
+
+
+ {% endif %}
+{% endblock %}
diff --git a/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/usage.csv b/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/usage.csv
index 79acd2c71..3ea6f81a4 100644
--- a/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/usage.csv
+++ b/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/usage.csv
@@ -1,8 +1,10 @@
Usage Report For Period:,{{datetime_start|date:"b. d Y H:i"}},/,{{datetime_end|date:"b. d Y H:i"}}
Active Instances:,{{global_summary.total_active_instances|default:'-'}}
-This month's VCPU-Hours:,{{global_summary.total_cpu_usage|floatformat|default:'-'}}
-This month's GB-Hours:,{{global_summary.total_disk_usage|floatformat|default:'-'}}
+Active Memory (MB):,{{global_summary.total_active_memory_mb|default:'-'}}
+This month's VCPU-Hours:,{{global_summary.total_vcpus_usage|floatformat|default:'-'}}
+This month's GB-Hours:,{{global_summary.total_local_gb_usage|floatformat|default:'-'}}
+This month's MemoryMB-Hours:,{{global_summary.total_memory_mb_usage|floatformat|default:'-'}}
-Name,UserId,VCPUs,RamMB,DiskGB,Flavor,Usage(Hours),Uptime(Seconds),State
-{% for usage in usage_list %}{% for instance in usage.instances %}{{instance.name|addslashes}},{{instance.user_id|addslashes}},{{instance.vcpus|addslashes}},{{instance.ram_size|addslashes}},{{instance.disk_size|addslashes}},{{instance.flavor|addslashes}},{{instance.hours}},{{instance.uptime}},{{instance.state|capfirst|addslashes}}{% endfor %}
+Tenant,Name,VCPUs,RamMB,DiskGB,Usage(Hours),Uptime(Seconds),State
+{% for usage in usage_list %}{% for server_usage in usage.server_usages %}{{server_usage.tenant_id|addslashes}},{{server_usage.name|addslashes}},{{server_usage.vcpus|addslashes}},{{server_usage.memory_mb|addslashes}},{{server_usage.local_gb|addslashes}},{{server_usage.hours}},{{server_usage.uptime}},{{server_usage.state|capfirst|addslashes}}{% endfor %}
{% endfor %}
diff --git a/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/usage.html b/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/usage.html
index da4c6aae2..ad95070bb 100644
--- a/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/usage.html
+++ b/horizon/horizon/dashboards/syspanel/templates/syspanel/tenants/usage.html
@@ -1,69 +1,39 @@
-{% extends 'syspanel/base.html' %}
+{% extends 'syspanel/tenants/base_usage.html' %}
{% load i18n sizeformat %}
{% block title %}Usage Overview{% endblock %}
+{% block activity_list %}
+ {% if instances %}
+
-{% block page_header %}
- {# to make searchable false, just remove it from the include statement #}
- {% include "horizon/common/_page_header.html" with title=_("System Panel Overview") %}
-{% endblock page_header %}
-
-{% block syspanel_main %}
-{% if external_links %}
-
-
{% trans "Monitoring" %}:
-
- {% for link in external_links %}
- - {{ link.0}}
- {% endfor %}
-
-
-{% endif %}
-
-
-
-
- {% trans "Active Instances" %}: {{ global_summary.total_active_instances|default:'-' }}
- {% trans "This month's VCPU-Hours" %}: {{ global_summary.total_cpu_usage|floatformat|default:'-' }}
- {% trans "This month's GB-Hours" %}: {{ global_summary.total_disk_usage|floatformat|default:'-' }}
-
-
-{% if usage_list %}
-
-