diff --git a/openstack_dashboard/dashboards/admin/instances/tests.py b/openstack_dashboard/dashboards/admin/instances/tests.py index 6cc70448a..5b39f4a03 100644 --- a/openstack_dashboard/dashboards/admin/instances/tests.py +++ b/openstack_dashboard/dashboards/admin/instances/tests.py @@ -171,11 +171,8 @@ class InstanceViewTest(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('flavor_list', 'server_list',), api.keystone: ('tenant_list',)}) def test_index_options_after_migrate(self): - servers = self.servers.list() - server1 = servers[0] - server1.status = "VERIFY_RESIZE" - server2 = servers[2] - server2.status = "VERIFY_RESIZE" + server = self.servers.first() + server.status = "VERIFY_RESIZE" api.keystone.tenant_list(IsA(http.HttpRequest)) \ .AndReturn(self.tenants.list()) search_opts = {'marker': None, 'paginate': True} diff --git a/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.csv b/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.csv index 76b65379b..686129b20 100644 --- a/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.csv +++ b/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.csv @@ -1,7 +1,10 @@ -{% load i18n %}{% trans "Usage Report For Period" %}:,{{ usage.start|date:"b. d Y" }},{{ usage.end|date:"b. d Y" }} -{% trans "Active Instances" %}:,{{ usage.summary.instances }} -{% trans "CPU-HRs Used" %}:,{{ usage.summary.vcpu_hours|floatformat:2 }} -{% trans "Total Active RAM (MB)" %}:,{{ usage.summary.memory_mb }} -{% trans "Total Disk Size" %}:,{{ usage.summary.local_gb }} -{% trans "Total Disk Usage" %}:,{{ usage.summary.disk_gb_hours|floatformat:2 }} +Usage Report For Period:,{{ usage.start|date:"b. d Y" }},{{ usage.end|date:"b. d Y" }} +Active Instances:,{{ usage.summary.instances }} +CPU-HRs Used:,{{ usage.summary.vcpu_hours|floatformat:2 }} +Total Active RAM (MB):,{{ usage.summary.memory_mb }} +Total Disk Size:,{{ usage.summary.local_gb }} +Total Disk Usage:,{{ usage.summary.disk_gb_hours|floatformat:2 }} +Tenant,VCPUs,RamMB,DiskGB,Usage(Hours) +{% for u in usage.usage_list %}{{ u.tenant_id|addslashes }},{{ u.vcpus|addslashes }},{{ u.memory_mb|addslashes }},{{u.local_gb|addslashes }},{{ u.vcpu_hours|floatformat:2}} +{% endfor %} diff --git a/openstack_dashboard/dashboards/admin/overview/tests.py b/openstack_dashboard/dashboards/admin/overview/tests.py index 3fa11afb6..692888513 100644 --- a/openstack_dashboard/dashboards/admin/overview/tests.py +++ b/openstack_dashboard/dashboards/admin/overview/tests.py @@ -38,7 +38,6 @@ INDEX_URL = reverse('horizon:project:overview:index') class UsageViewTests(test.BaseAdminViewTests): - @test.create_stubs({api.nova: ('usage_list',), quotas: ('tenant_quota_usages',), api.keystone: ('tenant_list',)}) @@ -76,26 +75,24 @@ class UsageViewTests(test.BaseAdminViewTests): api.keystone: ('tenant_list',)}) def test_usage_csv(self): now = timezone.now() - usage_obj = [api.nova.NovaUsage(u) for u in self.usages.list()] + usage_obj = api.nova.NovaUsage(self.usages.first()) quota_data = self.quota_usages.first() api.keystone.tenant_list(IsA(http.HttpRequest)) \ .AndReturn(self.tenants.list()) api.nova.usage_list(IsA(http.HttpRequest), datetime.datetime(now.year, now.month, 1, 0, 0, 0), Func(usage.almost_now)) \ - .AndReturn(usage_obj) + .AndReturn([usage_obj, usage_obj]) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) self.mox.ReplayAll() csv_url = reverse('horizon:admin:overview:index') + "?format=csv" res = self.client.get(csv_url) self.assertTemplateUsed(res, 'admin/overview/usage.csv') self.assertTrue(isinstance(res.context['usage'], usage.GlobalUsage)) - hdr = 'Project Name,VCPUs,Ram (MB),Disk (GB),Usage (Hours)' - self.assertContains(res, '%s\r\n' % (hdr)) - for obj in usage_obj: - row = u'{0},{1},{2},{3},{4:.2f}\r\n'.format(obj.project_name, - obj.vcpus, - obj.memory_mb, - obj.disk_gb_hours, - obj.vcpu_hours) - self.assertContains(res, row) + hdr = 'Tenant,VCPUs,RamMB,DiskGB,Usage(Hours)' + row = '%s,%s,%s,%s,%.2f' % (usage_obj.tenant_id, + usage_obj.vcpus, + usage_obj.memory_mb, + usage_obj.disk_gb_hours, + usage_obj.vcpu_hours) + self.assertContains(res, '%s\n%s\n%s\n' % (hdr, row, row)) diff --git a/openstack_dashboard/dashboards/admin/overview/views.py b/openstack_dashboard/dashboards/admin/overview/views.py index c837e9dff..a05e6bf68 100644 --- a/openstack_dashboard/dashboards/admin/overview/views.py +++ b/openstack_dashboard/dashboards/admin/overview/views.py @@ -18,38 +18,19 @@ # License for the specific language governing permissions and limitations # under the License. -from django import VERSION from django.conf import settings -from django.template.defaultfilters import floatformat from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from openstack_dashboard import api from openstack_dashboard import usage -from openstack_dashboard.usage.base import BaseCsvResponse - - -class GlobalUsageCsvRenderer(BaseCsvResponse): - - columns = [_("Project Name"), _("VCPUs"), _("Ram (MB)"), - _("Disk (GB)"), _("Usage (Hours)")] - - def get_row_data(self): - - for u in self.context['usage'].usage_list: - yield (u.project_name or u.tenant_id, - u.vcpus, - u.memory_mb, - u.local_gb, - floatformat(u.vcpu_hours, 2)) class GlobalOverview(usage.UsageView): table_class = usage.GlobalUsageTable usage_class = usage.GlobalUsage template_name = 'admin/overview/usage.html' - csv_response_class = GlobalUsageCsvRenderer def get_context_data(self, **kwargs): context = super(GlobalOverview, self).get_context_data(**kwargs) @@ -58,17 +39,17 @@ class GlobalOverview(usage.UsageView): def get_data(self): data = super(GlobalOverview, self).get_data() - # Pre-fill project names + # Pre-fill tenant names try: - projects = api.keystone.tenant_list(self.request) + tenants = api.keystone.tenant_list(self.request) except: - projects = [] + tenants = [] exceptions.handle(self.request, _('Unable to retrieve project list.')) for instance in data: - project = filter(lambda t: t.id == instance.tenant_id, projects) - if project: - instance.project_name = getattr(project[0], "name", None) + tenant = filter(lambda t: t.id == instance.tenant_id, tenants) + if tenant: + instance.tenant_name = getattr(tenant[0], "name", None) else: - instance.project_name = None + instance.tenant_name = None return data diff --git a/openstack_dashboard/dashboards/admin/projects/urls.py b/openstack_dashboard/dashboards/admin/projects/urls.py index 79b93275e..59369302c 100644 --- a/openstack_dashboard/dashboards/admin/projects/urls.py +++ b/openstack_dashboard/dashboards/admin/projects/urls.py @@ -20,7 +20,7 @@ from django.conf.urls.defaults import patterns, url -from .views import (IndexView, ProjectUsageView, +from .views import (IndexView, TenantUsageView, CreateProjectView, UpdateProjectView, CreateUserView) @@ -31,7 +31,7 @@ urlpatterns = patterns('', url(r'^(?P[^/]+)/update/$', UpdateProjectView.as_view(), name='update'), url(r'^(?P[^/]+)/usage/$', - ProjectUsageView.as_view(), name='usage'), + TenantUsageView.as_view(), name='usage'), url(r'^(?P[^/]+)/create_user/$', CreateUserView.as_view(), name='create_user'), ) diff --git a/openstack_dashboard/dashboards/admin/projects/views.py b/openstack_dashboard/dashboards/admin/projects/views.py index 9b2d31ee6..cded04a5f 100644 --- a/openstack_dashboard/dashboards/admin/projects/views.py +++ b/openstack_dashboard/dashboards/admin/projects/views.py @@ -116,13 +116,13 @@ class UsersView(tables.MultiTableView): return context -class ProjectUsageView(usage.UsageView): - table_class = usage.ProjectUsageTable - usage_class = usage.ProjectUsage +class TenantUsageView(usage.UsageView): + table_class = usage.TenantUsageTable + usage_class = usage.TenantUsage template_name = 'admin/projects/usage.html' def get_data(self): - super(ProjectUsageView, self).get_data() + super(TenantUsageView, self).get_data() return self.usage.get_instances() diff --git a/openstack_dashboard/dashboards/project/overview/templates/overview/usage.csv b/openstack_dashboard/dashboards/project/overview/templates/overview/usage.csv index 0baf1867d..3e2ecd5b2 100644 --- a/openstack_dashboard/dashboards/project/overview/templates/overview/usage.csv +++ b/openstack_dashboard/dashboards/project/overview/templates/overview/usage.csv @@ -1,9 +1,11 @@ -{% load i18n %}{% trans "Usage Report For Period" %}:,{{ usage.start|date:"b. d Y" }},{{ usage.end|date:"b. d Y" }} -{% trans "Project ID" %}:,{{ usage.project_id }} -{% trans "Project Name" %}:,{{ usage.project_name }} -{% trans "Total Active VCPUs" %}:,{{ usage.summary.instances }} -{% trans "CPU-HRs Used" %}:,{{ usage.summary.vcpu_hours|floatformat:2 }} -{% trans "Total Active Ram (MB)" %}:,{{ usage.summary.memory_mb }} -{% trans "Total Disk Size" %}:,{{ usage.summary.local_gb }} -{% trans "Total Disk Usage" %}:,{{ usage.summary.disk_gb_hours|floatformat:2 }} +Usage Report For Period:,{{ usage.start|date:"b. d Y" }},{{ usage.end|date:"b. d Y" }} +Tenant ID:,{{ usage.tenant_id }} +Total Active VCPUs:,{{ usage.summary.instances }} +CPU-HRs Used:,{{ usage.summary.vcpu_hours|floatformat:2 }} +Total Active Ram (MB):,{{ usage.summary.memory_mb }} +Total Disk Size:,{{ usage.summary.local_gb }} +Total Disk Usage:,{{ usage.summary.disk_gb_hours|floatformat:2 }} +Name,VCPUs,RamMB,DiskGB,Usage(Hours),Uptime(Seconds),State +{% for s in usage.get_instances %}{{ s.name|addslashes }},{{ s.vcpus|addslashes }},{{ s.memory_mb|addslashes }},{{s.local_gb|addslashes }},{{ s.hours|floatformat:2 }},{{ s.uptime }},{{ s.state|capfirst|addslashes }} +{% endfor %} diff --git a/openstack_dashboard/dashboards/project/overview/tests.py b/openstack_dashboard/dashboards/project/overview/tests.py index 9506d74ab..386ce88d2 100644 --- a/openstack_dashboard/dashboards/project/overview/tests.py +++ b/openstack_dashboard/dashboards/project/overview/tests.py @@ -36,27 +36,22 @@ INDEX_URL = reverse('horizon:project:overview:index') class UsageViewTests(test.TestCase): - - @test.create_stubs({api.nova: ('usage_get',), - quotas: ('tenant_quota_usages',), - api.keystone: ('tenant_get',)}) def test_usage(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) quota_data = self.quota_usages.first() - project = self.tenants.first() + self.mox.StubOutWithMock(api.nova, 'usage_get') + self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, datetime.datetime(now.year, now.month, 1, 0, 0, 0), Func(usage.almost_now)) \ .AndReturn(usage_obj) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) - api.keystone.tenant_get(IsA(http.HttpRequest), - project.id).AndReturn(project) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:overview:index')) self.assertTemplateUsed(res, 'project/overview/usage.html') - self.assertTrue(isinstance(res.context['usage'], usage.ProjectUsage)) + self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage)) self.assertContains(res, 'form-horizontal') def test_unauthorized(self): @@ -78,14 +73,12 @@ class UsageViewTests(test.TestCase): self.assertMessageCount(res, error=1) self.assertContains(res, 'Unauthorized:') - @test.create_stubs({api.nova: ('usage_get',), - quotas: ('tenant_quota_usages',), - api.keystone: ('tenant_get',)}) def test_usage_csv(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) quota_data = self.quota_usages.first() - project = self.tenants.first() + self.mox.StubOutWithMock(api.nova, 'usage_get') + self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, @@ -93,20 +86,18 @@ class UsageViewTests(test.TestCase): Func(usage.almost_now)) \ .AndReturn(usage_obj) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) - api.keystone.tenant_get(IsA(http.HttpRequest), - project.id).AndReturn(project) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:overview:index') + "?format=csv") self.assertTemplateUsed(res, 'project/overview/usage.csv') - self.assertTrue(isinstance(res.context['usage'], usage.ProjectUsage)) + self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage)) - @test.create_stubs({api.nova: ('usage_get',), - quotas: ('tenant_quota_usages',)}) def test_usage_exception_usage(self): now = timezone.now() quota_data = self.quota_usages.first() + self.mox.StubOutWithMock(api.nova, 'usage_get') + self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, @@ -120,13 +111,11 @@ class UsageViewTests(test.TestCase): self.assertTemplateUsed(res, 'project/overview/usage.html') self.assertEqual(res.context['usage'].usage_list, []) - @test.create_stubs({api.nova: ('usage_get',), - quotas: ('tenant_quota_usages',), - api.keystone: ('tenant_get',)}) def test_usage_exception_quota(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - project = self.tenants.first() + self.mox.StubOutWithMock(api.nova, 'usage_get') + self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, @@ -135,22 +124,18 @@ class UsageViewTests(test.TestCase): .AndReturn(usage_obj) quotas.tenant_quota_usages(IsA(http.HttpRequest))\ .AndRaise(self.exceptions.nova) - api.keystone.tenant_get(IsA(http.HttpRequest), - project.id).AndReturn(project) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:overview:index')) self.assertTemplateUsed(res, 'project/overview/usage.html') self.assertEqual(res.context['usage'].quotas, {}) - @test.create_stubs({api.nova: ('usage_get',), - quotas: ('tenant_quota_usages',), - api.keystone: ('tenant_get',)}) def test_usage_default_tenant(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) quota_data = self.quota_usages.first() - project = self.tenants.first() + self.mox.StubOutWithMock(api.nova, 'usage_get') + self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, @@ -158,10 +143,8 @@ class UsageViewTests(test.TestCase): Func(usage.almost_now)) \ .AndReturn(usage_obj) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) - api.keystone.tenant_get(IsA(http.HttpRequest), - project.id).AndReturn(project) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:overview:index')) self.assertTemplateUsed(res, 'project/overview/usage.html') - self.assertTrue(isinstance(res.context['usage'], usage.ProjectUsage)) + self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage)) diff --git a/openstack_dashboard/dashboards/project/overview/views.py b/openstack_dashboard/dashboards/project/overview/views.py index 498b6a12d..32fc3162f 100644 --- a/openstack_dashboard/dashboards/project/overview/views.py +++ b/openstack_dashboard/dashboards/project/overview/views.py @@ -18,38 +18,15 @@ # License for the specific language governing permissions and limitations # under the License. -from django import VERSION -from django.template.defaultfilters import floatformat, capfirst -from django.utils.translation import ugettext as _ from django.views.generic import TemplateView from openstack_dashboard import usage -from openstack_dashboard.usage.base import BaseCsvResponse - - -class ProjectUsageCsvRenderer(BaseCsvResponse): - - columns = [_("Instance Name"), _("VCPUs"), _("Ram (MB)"), - _("Disk (GB)"), _("Usage (Hours)"), - _("Uptime(Seconds)"), _("State")] - - def get_row_data(self): - - for inst in self.context['usage'].get_instances(): - yield (inst['name'], - inst['vcpus'], - inst['memory_mb'], - inst['local_gb'], - floatformat(inst['hours'], 2), - inst['uptime'], - capfirst(inst['state'])) class ProjectOverview(usage.UsageView): - table_class = usage.ProjectUsageTable - usage_class = usage.ProjectUsage + table_class = usage.TenantUsageTable + usage_class = usage.TenantUsage template_name = 'project/overview/usage.html' - csv_response_class = ProjectUsageCsvRenderer def get_data(self): super(ProjectOverview, self).get_data() diff --git a/openstack_dashboard/dashboards/project/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/tests.py index 10e2cbd1e..bf8f5f973 100644 --- a/openstack_dashboard/dashboards/project/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/tests.py @@ -609,8 +609,7 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get',), api.nova: ('server_list',)}) def test_edit_attachments(self): volume = self.volumes.first() - servers = [s for s in self.servers.list() - if s.tenant_id == self.request.user.tenant_id] + servers = self.servers.list() cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume) api.nova.server_list(IsA(http.HttpRequest)).AndReturn([servers, False]) @@ -633,8 +632,7 @@ class VolumeViewTests(test.TestCase): settings.OPENSTACK_HYPERVISOR_FEATURES['can_set_mount_point'] = False volume = self.volumes.first() - servers = [s for s in self.servers.list() - if s.tenant_id == self.request.user.tenant_id] + servers = self.servers.list() cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume) api.nova.server_list(IsA(http.HttpRequest)).AndReturn([servers, False]) @@ -651,15 +649,13 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_get',), api.nova: ('server_get', 'server_list',)}) def test_edit_attachments_attached_volume(self): - servers = [s for s in self.servers.list() - if s.tenant_id == self.request.user.tenant_id] - server = servers[0] + server = self.servers.first() volume = self.volumes.list()[0] cinder.volume_get(IsA(http.HttpRequest), volume.id) \ .AndReturn(volume) api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn([servers, False]) + .AndReturn([self.servers.list(), False]) self.mox.ReplayAll() diff --git a/openstack_dashboard/test/api_tests/nova_tests.py b/openstack_dashboard/test/api_tests/nova_tests.py index 16b67b13b..71e354f8e 100644 --- a/openstack_dashboard/test/api_tests/nova_tests.py +++ b/openstack_dashboard/test/api_tests/nova_tests.py @@ -33,7 +33,6 @@ from openstack_dashboard.test import helpers as test class ServerWrapperTests(test.TestCase): - def test_get_base_attribute(self): server = api.nova.Server(self.servers.first(), self.request) self.assertEqual(server.id, self.servers.first().id) @@ -42,7 +41,7 @@ class ServerWrapperTests(test.TestCase): image = self.images.first() self.mox.StubOutWithMock(api.glance, 'image_get') api.glance.image_get(IsA(http.HttpRequest), - image.id).AndReturn(image) + image.id).AndReturn(image) self.mox.ReplayAll() server = api.nova.Server(self.servers.first(), self.request) @@ -50,7 +49,6 @@ class ServerWrapperTests(test.TestCase): class ComputeApiTests(test.APITestCase): - def test_server_reboot(self): server = self.servers.first() HARDNESS = servers.REBOOT_HARD @@ -101,7 +99,7 @@ class ComputeApiTests(test.APITestCase): novaclient = self.stub_novaclient() novaclient.servers = self.mox.CreateMockAnything() novaclient.servers.get_spice_console(server.id, - console_type).AndReturn(console) + console_type).AndReturn(console) self.mox.ReplayAll() ret_val = api.nova.server_spice_console(self.request, @@ -150,8 +148,7 @@ class ComputeApiTests(test.APITestCase): novaclient.servers.list(True, {'all_tenants': True, 'marker': None, - 'limit': page_size + 1}). \ - AndReturn(servers[:page_size + 1]) + 'limit': page_size + 1}).AndReturn(servers) self.mox.ReplayAll() ret_val, has_more = api.nova.server_list(self.request, diff --git a/openstack_dashboard/test/test_data/keystone_data.py b/openstack_dashboard/test/test_data/keystone_data.py index d75fb6eee..ea61f8188 100644 --- a/openstack_dashboard/test/test_data/keystone_data.py +++ b/openstack_dashboard/test/test_data/keystone_data.py @@ -139,14 +139,9 @@ def data(TEST): 'name': 'disabled_tenant', 'description': "a disabled test tenant.", 'enabled': False} - tenant_dict_unicode = {'id': "3", - 'name': u'\u4e91\u89c4\u5219', - 'description': "an unicode-named tenant.", - 'enabled': True} tenant = tenants.Tenant(tenants.TenantManager, tenant_dict) disabled_tenant = tenants.Tenant(tenants.TenantManager, tenant_dict_2) - tenant_unicode = tenants.Tenant(tenants.TenantManager, tenant_dict_unicode) - TEST.tenants.add(tenant, disabled_tenant, tenant_unicode) + TEST.tenants.add(tenant, disabled_tenant) TEST.tenant = tenant # Your "current" tenant tomorrow = datetime_safe.datetime.now() + timedelta(days=1) diff --git a/openstack_dashboard/test/test_data/nova_data.py b/openstack_dashboard/test/test_data/nova_data.py index 01bf8a132..992533241 100644 --- a/openstack_dashboard/test/test_data/nova_data.py +++ b/openstack_dashboard/test/test_data/nova_data.py @@ -361,8 +361,6 @@ def data(TEST): TEST.limits = limits # Servers - tenant3 = TEST.tenants.list()[2] - vals = {"host": "http://nova.example.com:8774", "name": "server_1", "status": "ACTIVE", @@ -379,13 +377,7 @@ def data(TEST): "server_id": "2"}) server_2 = servers.Server(servers.ServerManager(None), json.loads(SERVER_DATA % vals)['server']) - vals.update({"name": u'\u4e91\u89c4\u5219', - "status": "ACTIVE", - "tenant_id": tenant3.id, - "server_id": "3"}) - server_3 = servers.Server(servers.ServerManager(None), - json.loads(SERVER_DATA % vals)['server']) - TEST.servers.add(server_1, server_2, server_3) + TEST.servers.add(server_1, server_2) # VNC Console Data console = {u'console': {u'url': u'http://example.com:6080/vnc_auto.html', @@ -442,17 +434,6 @@ def data(TEST): json.loads(USAGE_DATA % usage_vals)) TEST.usages.add(usage_obj) - # Usage - usage_2_vals = {"tenant_id": tenant3.id, - "instance_name": server_3.name, - "flavor_name": flavor_1.name, - "flavor_vcpus": flavor_1.vcpus, - "flavor_disk": flavor_1.disk, - "flavor_ram": flavor_1.ram} - usage_obj_2 = usage.Usage(usage.UsageManager(None), - json.loads(USAGE_DATA % usage_2_vals)) - TEST.usages.add(usage_obj_2) - volume_snapshot = vol_snaps.Snapshot(vol_snaps.SnapshotManager(None), {'id': '40f3fabf-3613-4f5e-90e5-6c9a08333fc3', 'display_name': 'test snapshot', diff --git a/openstack_dashboard/test/tests/quotas.py b/openstack_dashboard/test/tests/quotas.py index db6b7f912..1c65f40ca 100644 --- a/openstack_dashboard/test/tests/quotas.py +++ b/openstack_dashboard/test/tests/quotas.py @@ -56,8 +56,6 @@ class QuotaTests(test.APITestCase): quotas: ('is_service_enabled',), cinder: ('volume_list', 'tenant_quota_get',)}) def test_tenant_quota_usages(self): - servers = [s for s in self.servers.list() - if s.tenant_id == self.request.user.tenant_id] quotas.is_service_enabled(IsA(http.HttpRequest), 'volume').AndReturn(True) api.nova.flavor_list(IsA(http.HttpRequest)) \ @@ -67,7 +65,7 @@ class QuotaTests(test.APITestCase): api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn([servers, False]) + .AndReturn([self.servers.list(), False]) cinder.volume_list(IsA(http.HttpRequest)) \ .AndReturn(self.volumes.list()) cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \ @@ -87,8 +85,6 @@ class QuotaTests(test.APITestCase): api.network: ('tenant_floating_ip_list',), quotas: ('is_service_enabled',)}) def test_tenant_quota_usages_without_volume(self): - servers = [s for s in self.servers.list() - if s.tenant_id == self.request.user.tenant_id] quotas.is_service_enabled(IsA(http.HttpRequest), 'volume').AndReturn(False) api.nova.flavor_list(IsA(http.HttpRequest)) \ @@ -98,7 +94,7 @@ class QuotaTests(test.APITestCase): api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn([servers, False]) + .AndReturn([self.servers.list(), False]) self.mox.ReplayAll() @@ -147,8 +143,6 @@ class QuotaTests(test.APITestCase): def test_tenant_quota_usages_unlimited_quota(self): inf_quota = self.quotas.first() inf_quota['ram'] = -1 - servers = [s for s in self.servers.list() - if s.tenant_id == self.request.user.tenant_id] quotas.is_service_enabled(IsA(http.HttpRequest), 'volume').AndReturn(True) @@ -159,7 +153,7 @@ class QuotaTests(test.APITestCase): api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \ .AndReturn(self.floating_ips.list()) api.nova.server_list(IsA(http.HttpRequest)) \ - .AndReturn([servers, False]) + .AndReturn([self.servers.list(), False]) cinder.volume_list(IsA(http.HttpRequest)) \ .AndReturn(self.volumes.list()) cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \ diff --git a/openstack_dashboard/usage/__init__.py b/openstack_dashboard/usage/__init__.py index 2edc6445c..e74fc056a 100644 --- a/openstack_dashboard/usage/__init__.py +++ b/openstack_dashboard/usage/__init__.py @@ -14,6 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -from .base import BaseUsage, ProjectUsage, GlobalUsage, almost_now +from .base import BaseUsage, TenantUsage, GlobalUsage, almost_now from .views import UsageView -from .tables import BaseUsageTable, ProjectUsageTable, GlobalUsageTable +from .tables import BaseUsageTable, TenantUsageTable, GlobalUsageTable diff --git a/openstack_dashboard/usage/base.py b/openstack_dashboard/usage/base.py index cae12c132..829a85bfe 100644 --- a/openstack_dashboard/usage/base.py +++ b/openstack_dashboard/usage/base.py @@ -1,13 +1,9 @@ from __future__ import division from calendar import monthrange -from csv import writer, DictWriter import datetime import logging -from StringIO import StringIO -from django import template as django_template, VERSION -from django.http.response import HttpResponse from django.utils.translation import ugettext_lazy as _ from django.utils import timezone @@ -31,8 +27,8 @@ def almost_now(input_time): class BaseUsage(object): show_terminated = False - def __init__(self, request, project_id=None): - self.project_id = project_id or request.user.tenant_id + def __init__(self, request, tenant_id=None): + self.tenant_id = tenant_id or request.user.tenant_id self.request = request self.summary = {} self.usage_list = [] @@ -105,9 +101,9 @@ class BaseUsage(object): _("You are viewing data for the future, " "which may or may not exist.")) - for project_usage in self.usage_list: - project_summary = project_usage.get_summary() - for key, value in project_summary.items(): + for tenant_usage in self.usage_list: + tenant_summary = tenant_usage.get_summary() + for key, value in tenant_summary.items(): self.summary.setdefault(key, 0) self.summary[key] += value @@ -134,7 +130,7 @@ class GlobalUsage(BaseUsage): return api.nova.usage_list(self.request, start, end) -class ProjectUsage(BaseUsage): +class TenantUsage(BaseUsage): attrs = ('memory_mb', 'vcpus', 'uptime', 'hours', 'local_gb') @@ -143,9 +139,7 @@ class ProjectUsage(BaseUsage): self.show_terminated) instances = [] terminated_instances = [] - usage = api.nova.usage_get(self.request, self.project_id, start, end) - project = api.keystone.tenant_get(self.request, self.project_id) - self.project_name = project.name + usage = api.nova.usage_get(self.request, self.tenant_id, start, end) # Attribute may not exist if there are no instances if hasattr(usage, 'server_usages'): now = self.today @@ -161,130 +155,3 @@ class ProjectUsage(BaseUsage): instances.append(server_usage) usage.server_usages = instances return (usage,) - - -class CsvDataMixin(object): - - """ - CSV data Mixin - provides handling for CSV data - - .. attribute:: columns - - A list of CSV column definitions. If omitted - no column titles - will be shown in the result file. Optional. - """ - def __init__(self): - self.out = StringIO() - super(CsvDataMixin, self).__init__() - if hasattr(self, "columns"): - self.writer = DictWriter(self.out, map(self.encode, self.columns)) - self.is_dict = True - else: - self.writer = writer(self.out) - self.is_dict = False - - def write_csv_header(self): - if self.is_dict: - try: - self.writer.writeheader() - except AttributeError: - # For Python<2.7 - self.writer.writerow(dict(zip( - self.writer.fieldnames, - self.writer.fieldnames))) - - def write_csv_row(self, args): - if self.is_dict: - self.writer.writerow(dict(zip( - self.writer.fieldnames, map(self.encode, args)))) - else: - self.writer.writerow(map(self.encode, args)) - - def encode(self, value): - # csv and StringIO cannot work with mixed encodings, - # so encode all with utf-8 - return unicode(value).encode('utf-8') - - -class BaseCsvResponse(CsvDataMixin, HttpResponse): - - """ - Base CSV response class. Provides handling of CSV data. - - """ - - def __init__(self, request, template, context, content_type, **kwargs): - super(BaseCsvResponse, self).__init__() - self['Content-Disposition'] = 'attachment; filename="%s"' % ( - kwargs.get("filename", "export.csv"),) - self['Content-Type'] = content_type - self.context = context - self.header = None - if template: - # Display some header info if provided as a template - header_template = django_template.loader.get_template(template) - context = django_template.RequestContext(request, self.context) - self.header = header_template.render(context) - - if self.header: - self.out.write(self.encode(self.header)) - - self.write_csv_header() - - for row in self.get_row_data(): - self.write_csv_row(row) - - self.out.flush() - self.content = self.out.getvalue() - self.out.close() - - def get_row_data(self): - raise NotImplementedError("You must define a get_row_data method on %s" - % self.__class__.__name__) - -if VERSION >= (1, 5, 0): - - from django.http import StreamingHttpResponse - - class BaseCsvStreamingResponse(CsvDataMixin, StreamingHttpResponse): - - """ - Base CSV Streaming class. Provides streaming response for CSV data. - """ - - def __init__(self, request, template, context, content_type, **kwargs): - super(BaseCsvStreamingResponse, self).__init__() - self['Content-Disposition'] = 'attachment; filename="%s"' % ( - kwargs.get("filename", "export.csv"),) - self['Content-Type'] = content_type - self.context = context - self.header = None - if template: - # Display some header info if provided as a template - header_template = django_template.loader.get_template(template) - context = django_template.RequestContext(request, self.context) - self.header = header_template.render(context) - - self._closable_objects.append(self.out) - - self.streaming_content = self.get_content() - - def buffer(self): - buf = self.out.getvalue() - self.out.truncate(0) - return buf - - def get_content(self): - if self.header: - self.out.write(self.encode(self.header)) - - self.write_csv_header() - yield self.buffer() - - for row in self.get_row_data(): - self.write_csv_row(row) - yield self.buffer() - - def get_row_data(self): - raise NotImplementedError("You must define a get_row_data method " - "on %s" % self.__class__.__name__) diff --git a/openstack_dashboard/usage/tables.py b/openstack_dashboard/usage/tables.py index cc61c839e..5df8342a6 100644 --- a/openstack_dashboard/usage/tables.py +++ b/openstack_dashboard/usage/tables.py @@ -27,7 +27,7 @@ class BaseUsageTable(tables.DataTable): class GlobalUsageTable(BaseUsageTable): - project = tables.Column('project_name', verbose_name=_("Project Name")) + tenant = tables.Column('tenant_name', verbose_name=_("Project Name")) disk_hours = tables.Column('disk_gb_hours', verbose_name=_("Disk GB Hours"), filters=(lambda v: floatformat(v, 2),)) @@ -38,7 +38,7 @@ class GlobalUsageTable(BaseUsageTable): class Meta: name = "global_usage" verbose_name = _("Usage Summary") - columns = ("project", "vcpus", "disk", "memory", + columns = ("tenant", "vcpus", "disk", "memory", "hours", "disk_hours") table_actions = (CSVSummary,) multi_select = False @@ -52,7 +52,7 @@ def get_instance_link(datum): return None -class ProjectUsageTable(BaseUsageTable): +class TenantUsageTable(BaseUsageTable): instance = tables.Column('name', verbose_name=_("Instance Name"), link=get_instance_link) @@ -64,7 +64,7 @@ class ProjectUsageTable(BaseUsageTable): return datum.get('instance_id', id(datum)) class Meta: - name = "project_usage" + name = "tenant_usage" verbose_name = _("Usage Summary") columns = ("instance", "vcpus", "disk", "memory", "uptime") table_actions = (CSVSummary,) diff --git a/openstack_dashboard/usage/views.py b/openstack_dashboard/usage/views.py index 953eccfb3..bb6cf635c 100644 --- a/openstack_dashboard/usage/views.py +++ b/openstack_dashboard/usage/views.py @@ -28,8 +28,8 @@ class UsageView(tables.DataTableView): return "text/html" def get_data(self): - project_id = self.kwargs.get('project_id', self.request.user.tenant_id) - self.usage = self.usage_class(self.request, project_id) + tenant_id = self.kwargs.get('tenant_id', self.request.user.tenant_id) + self.usage = self.usage_class(self.request, tenant_id) self.usage.summarize(*self.usage.get_date_range()) self.usage.get_quotas() self.kwargs['usage'] = self.usage @@ -43,14 +43,12 @@ class UsageView(tables.DataTableView): return context def render_to_response(self, context, **response_kwargs): + resp = self.response_class(request=self.request, + template=self.get_template_names(), + context=context, + content_type=self.get_content_type(), + **response_kwargs) if self.request.GET.get('format', 'html') == 'csv': - render_class = self.csv_response_class - response_kwargs.setdefault("filename", "usage.csv") - else: - render_class = self.response_class - resp = render_class(request=self.request, - template=self.get_template_names(), - context=context, - content_type=self.get_content_type(), - **response_kwargs) + resp['Content-Disposition'] = 'attachment; filename=usage.csv' + resp['Content-Type'] = 'text/csv' return resp