Enable quota data from multiple sources.

Now that there are multiple projects with quota data (cinder, quantum)
we need to accommodate that data being aggregated in a centralized
fashion. This commit takes care of that for nova + cinder, and paves
the way for quantum later.

Fixes bug 1070022.

Change-Id: Ifc68c2dc681b2a7b4e7787e0b1a7dca1a970fc36
This commit is contained in:
Gabriel Hurley 2012-10-23 22:25:18 -07:00
parent a0ad0d49a9
commit cdcd8e3df6
28 changed files with 626 additions and 431 deletions

View File

@ -140,7 +140,7 @@ horizon.Quota = {
element.removeClass('progress_bar_over'); element.removeClass('progress_bar_over');
} }
element.animate({width: update_width + "%"}, 300); element.animate({width: parseInt(update_width, 10) + "%"}, 300);
}, },
/* /*

View File

@ -45,3 +45,6 @@ class memoized(object):
def __get__(self, obj, objtype): def __get__(self, obj, objtype):
'''Support instance methods.''' '''Support instance methods.'''
return functools.partial(self.__call__, obj) return functools.partial(self.__call__, obj)
def __str__(self):
return str(self.func)

View File

@ -37,3 +37,4 @@ from openstack_dashboard.api.keystone import *
from openstack_dashboard.api.nova import * from openstack_dashboard.api.nova import *
from openstack_dashboard.api.swift import * from openstack_dashboard.api.swift import *
from openstack_dashboard.api.quantum import * from openstack_dashboard.api.quantum import *
from openstack_dashboard.api.cinder import *

View File

@ -18,6 +18,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from collections import Sequence
import logging import logging
from django.conf import settings from django.conf import settings
@ -92,6 +93,53 @@ class APIDictWrapper(object):
return default return default
class Quota(object):
"""Wrapper for individual limits in a quota."""
def __init__(self, name, limit):
self.name = name
self.limit = limit
def __repr__(self):
return "<Quota: (%s, %s)>" % (self.name, self.limit)
class QuotaSet(Sequence):
"""
Wrapper for client QuotaSet objects which turns the individual quotas
into Quota objects for easier handling/iteration.
`QuotaSet` objects support a mix of `list` and `dict` methods; you can use
the bracket notiation (`qs["my_quota"] = 0`) to add new quota values, and
use the `get` method to retrieve a specific quota, but otherwise it
behaves much like a list or tuple, particularly in supporting iteration.
"""
def __init__(self, apiresource=None):
self.items = []
if apiresource:
for k, v in apiresource._info.items():
if k == 'id':
continue
self[k] = v
def __setitem__(self, k, v):
v = int(v) if v is not None else v
q = Quota(k, v)
self.items.append(q)
def __getitem__(self, index):
return self.items[index]
def __len__(self):
return len(self.items)
def __repr__(self):
return repr(self.items)
def get(self, key, default=None):
match = [quota for quota in self.items if quota.name == key]
return match.pop() if len(match) else Quota(key, default)
def get_service_from_catalog(catalog, service_type): def get_service_from_catalog(catalog, service_type):
if catalog: if catalog:
for service in catalog: for service in catalog:
@ -116,3 +164,12 @@ def url_for(request, service_type, admin=False, endpoint_type=None):
raise exceptions.ServiceCatalogException(service_type) raise exceptions.ServiceCatalogException(service_type)
else: else:
raise exceptions.ServiceCatalogException(service_type) raise exceptions.ServiceCatalogException(service_type)
def is_service_enabled(request, service_type, service_name=None):
service = get_service_from_catalog(request.user.service_catalog,
service_type)
if service and service_name:
return service['name'] == service_name
else:
return service is not None

View File

@ -0,0 +1,115 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Openstack, LLC
# Copyright 2012 Nebula, Inc.
# Copyright (c) 2012 X.commerce, a business unit of eBay 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.
from __future__ import absolute_import
import logging
from django.conf import settings
from django.utils.translation import ugettext as _
from cinderclient.v1 import client as cinder_client
from openstack_dashboard.api.base import url_for
from openstack_dashboard.api import nova, QuotaSet
LOG = logging.getLogger(__name__)
# API static values
VOLUME_STATE_AVAILABLE = "available"
def cinderclient(request):
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
LOG.debug('cinderclient connection created using token "%s" and url "%s"' %
(request.user.token.id, url_for(request, 'volume')))
c = cinder_client.Client(request.user.username,
request.user.token.id,
project_id=request.user.tenant_id,
auth_url=url_for(request, 'volume'),
insecure=insecure)
c.client.auth_token = request.user.token.id
c.client.management_url = url_for(request, 'volume')
return c
def volume_list(request, search_opts=None):
"""
To see all volumes in the cloud as an admin you can pass in a special
search option: {'all_tenants': 1}
"""
return cinderclient(request).volumes.list(search_opts=search_opts)
def volume_get(request, volume_id):
volume_data = cinderclient(request).volumes.get(volume_id)
for attachment in volume_data.attachments:
if "server_id" in attachment:
instance = nova.server_get(request, attachment['server_id'])
attachment['instance_name'] = instance.name
else:
# Nova volume can occasionally send back error'd attachments
# the lack a server_id property; to work around that we'll
# give the attached instance a generic name.
attachment['instance_name'] = _("Unknown instance")
return volume_data
def volume_create(request, size, name, description, snapshot_id=None):
return cinderclient(request).volumes.create(size, display_name=name,
display_description=description, snapshot_id=snapshot_id)
def volume_delete(request, volume_id):
return cinderclient(request).volumes.delete(volume_id)
def volume_snapshot_get(request, snapshot_id):
return cinderclient(request).volume_snapshots.get(snapshot_id)
def volume_snapshot_list(request):
return cinderclient(request).volume_snapshots.list()
def volume_snapshot_create(request, volume_id, name, description):
return cinderclient(request).volume_snapshots.create(
volume_id, display_name=name, display_description=description)
def volume_snapshot_delete(request, snapshot_id):
return cinderclient(request).volume_snapshots.delete(snapshot_id)
def tenant_quota_get(request, tenant_id):
return QuotaSet(cinderclient(request).quotas.get(tenant_id))
def tenant_quota_update(request, tenant_id, **kwargs):
return cinderclient(request).quotas.update(tenant_id, **kwargs)
def default_quota_get(request, tenant_id):
return QuotaSet(cinderclient(request).quotas.defaults(tenant_id))

View File

@ -27,17 +27,14 @@ import logging
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from cinderclient.v1 import client as cinder_client
from novaclient.v1_1 import client as nova_client from novaclient.v1_1 import client as nova_client
from novaclient.v1_1 import security_group_rules as nova_rules from novaclient.v1_1 import security_group_rules as nova_rules
from novaclient.v1_1.security_groups import SecurityGroup as NovaSecurityGroup from novaclient.v1_1.security_groups import SecurityGroup as NovaSecurityGroup
from novaclient.v1_1.servers import REBOOT_HARD from novaclient.v1_1.servers import REBOOT_HARD
from horizon import exceptions
from horizon.utils.memoized import memoized from horizon.utils.memoized import memoized
from openstack_dashboard.api.base import (APIResourceWrapper, from openstack_dashboard.api.base import (APIResourceWrapper, QuotaSet,
APIDictWrapper, url_for) APIDictWrapper, url_for)
@ -56,32 +53,6 @@ class VNCConsole(APIDictWrapper):
_attrs = ['url', 'type'] _attrs = ['url', 'type']
class Quota(object):
"""Wrapper for individual limits in a quota."""
def __init__(self, name, limit):
self.name = name
self.limit = limit
def __repr__(self):
return "<Quota: (%s, %s)>" % (self.name, self.limit)
class QuotaSet(object):
"""Wrapper for novaclient.quotas.QuotaSet objects which wraps the
individual quotas inside Quota objects.
"""
def __init__(self, apiresource):
self.items = []
for k in apiresource._info.keys():
if k in ['id']:
continue
limit = apiresource._info[k]
v = int(limit) if limit is not None else limit
q = Quota(k, v)
self.items.append(q)
setattr(self, k, v)
class Server(APIResourceWrapper): class Server(APIResourceWrapper):
"""Simple wrapper around novaclient.server.Server """Simple wrapper around novaclient.server.Server
@ -117,7 +88,7 @@ class Server(APIResourceWrapper):
novaclient(self.request).servers.reboot(self.id, hardness) novaclient(self.request).servers.reboot(self.id, hardness)
class Usage(APIResourceWrapper): class NovaUsage(APIResourceWrapper):
"""Simple wrapper around contrib/simple_usage.py.""" """Simple wrapper around contrib/simple_usage.py."""
_attrs = ['start', 'server_usages', 'stop', 'tenant_id', _attrs = ['start', 'server_usages', 'stop', 'tenant_id',
'total_local_gb_usage', 'total_memory_mb_usage', 'total_local_gb_usage', 'total_memory_mb_usage',
@ -210,20 +181,6 @@ def novaclient(request):
return c return c
def cinderclient(request):
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
LOG.debug('cinderclient connection created using token "%s" and url "%s"' %
(request.user.token.id, url_for(request, 'volume')))
c = cinder_client.Client(request.user.username,
request.user.token.id,
project_id=request.user.tenant_id,
auth_url=url_for(request, 'volume'),
insecure=insecure)
c.client.auth_token = request.user.token.id
c.client.management_url = url_for(request, 'volume')
return c
def server_vnc_console(request, instance_id, console_type='novnc'): def server_vnc_console(request, instance_id, console_type='novnc'):
return VNCConsole(novaclient(request).servers.get_vnc_console(instance_id, return VNCConsole(novaclient(request).servers.get_vnc_console(instance_id,
console_type)['console']) console_type)['console'])
@ -408,68 +365,17 @@ def tenant_quota_update(request, tenant_id, **kwargs):
novaclient(request).quotas.update(tenant_id, **kwargs) novaclient(request).quotas.update(tenant_id, **kwargs)
def tenant_quota_defaults(request, tenant_id): def default_quota_get(request, tenant_id):
return QuotaSet(novaclient(request).quotas.defaults(tenant_id)) return QuotaSet(novaclient(request).quotas.defaults(tenant_id))
def usage_get(request, tenant_id, start, end): def usage_get(request, tenant_id, start, end):
return Usage(novaclient(request).usage.get(tenant_id, start, end)) return NovaUsage(novaclient(request).usage.get(tenant_id, start, end))
def usage_list(request, start, end): def usage_list(request, start, end):
return [Usage(u) for u in novaclient(request).usage.list(start, end, True)] return [NovaUsage(u) for u in
novaclient(request).usage.list(start, end, True)]
@memoized
def tenant_quota_usages(request):
"""
Builds a dictionary of current usage against quota for the current
project.
"""
instances = server_list(request)
floating_ips = tenant_floating_ip_list(request)
quotas = tenant_quota_get(request, request.user.tenant_id)
flavors = dict([(f.id, f) for f in flavor_list(request)])
volumes = volume_list(request)
usages = {'instances': {'flavor_fields': [], 'used': len(instances)},
'cores': {'flavor_fields': ['vcpus'], 'used': 0},
'gigabytes': {'used': sum([int(v.size) for v in volumes]),
'flavor_fields': []},
'volumes': {'used': len(volumes), 'flavor_fields': []},
'ram': {'flavor_fields': ['ram'], 'used': 0},
'floating_ips': {'flavor_fields': [], 'used': len(floating_ips)}}
for usage in usages:
for instance in instances:
used_flavor = instance.flavor['id']
if used_flavor not in flavors:
try:
flavors[used_flavor] = flavor_get(request, used_flavor)
except:
flavors[used_flavor] = {}
exceptions.handle(request, ignore=True)
for flavor_field in usages[usage]['flavor_fields']:
instance_flavor = flavors[used_flavor]
usages[usage]['used'] += getattr(instance_flavor,
flavor_field,
0)
usages[usage]['quota'] = getattr(quotas, usage)
if usages[usage]['quota'] is None:
usages[usage]['quota'] = float("inf")
usages[usage]['available'] = float("inf")
elif type(usages[usage]['quota']) is str:
usages[usage]['quota'] = int(usages[usage]['quota'])
else:
if type(usages[usage]['used']) is str:
usages[usage]['used'] = int(usages[usage]['used'])
usages[usage]['available'] = usages[usage]['quota'] - \
usages[usage]['used']
return usages
def security_group_list(request): def security_group_list(request):
@ -510,30 +416,28 @@ def virtual_interfaces_list(request, instance_id):
return novaclient(request).virtual_interfaces.list(instance_id) return novaclient(request).virtual_interfaces.list(instance_id)
def volume_list(request, search_opts=None): def get_x509_credentials(request):
""" return novaclient(request).certs.create()
To see all volumes in the cloud as an admin you can pass in a special
search option: {'all_tenants': 1}
"""
return cinderclient(request).volumes.list(search_opts=search_opts)
def volume_get(request, volume_id): def get_x509_root_certificate(request):
volume_data = cinderclient(request).volumes.get(volume_id) return novaclient(request).certs.get()
for attachment in volume_data.attachments:
if "server_id" in attachment:
instance = server_get(request, attachment['server_id'])
attachment['instance_name'] = instance.name
else:
# Nova volume can occasionally send back error'd attachments
# the lack a server_id property; to work around that we'll
# give the attached instance a generic name.
attachment['instance_name'] = _("Unknown instance")
return volume_data
def volume_instance_list(request, instance_id): def instance_volume_attach(request, volume_id, instance_id, device):
return novaclient(request).volumes.create_server_volume(instance_id,
volume_id,
device)
def instance_volume_detach(request, instance_id, att_id):
return novaclient(request).volumes.delete_server_volume(instance_id,
att_id)
def instance_volumes_list(request, instance_id):
from openstack_dashboard.api.cinder import cinderclient
volumes = novaclient(request).volumes.get_server_volumes(instance_id) volumes = novaclient(request).volumes.get_server_volumes(instance_id)
for volume in volumes: for volume in volumes:
@ -541,47 +445,3 @@ def volume_instance_list(request, instance_id):
volume.name = volume_data.display_name volume.name = volume_data.display_name
return volumes return volumes
def volume_create(request, size, name, description, snapshot_id=None):
return cinderclient(request).volumes.create(size, display_name=name,
display_description=description, snapshot_id=snapshot_id)
def volume_delete(request, volume_id):
cinderclient(request).volumes.delete(volume_id)
def volume_attach(request, volume_id, instance_id, device):
return novaclient(request).volumes.create_server_volume(instance_id,
volume_id,
device)
def volume_detach(request, instance_id, att_id):
novaclient(request).volumes.delete_server_volume(instance_id, att_id)
def volume_snapshot_get(request, snapshot_id):
return cinderclient(request).volume_snapshots.get(snapshot_id)
def volume_snapshot_list(request):
return cinderclient(request).volume_snapshots.list()
def volume_snapshot_create(request, volume_id, name, description):
return cinderclient(request).volume_snapshots.create(
volume_id, display_name=name, display_description=description)
def volume_snapshot_delete(request, snapshot_id):
cinderclient(request).volume_snapshots.delete(snapshot_id)
def get_x509_credentials(request):
return novaclient(request).certs.create()
def get_x509_root_certificate(request):
return novaclient(request).certs.get()

View File

@ -29,8 +29,9 @@ from mox import IsA, Func
from horizon.templatetags.sizeformat import mbformat from horizon.templatetags.sizeformat import mbformat
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.test import helpers as test
from openstack_dashboard import usage from openstack_dashboard import usage
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
INDEX_URL = reverse('horizon:project:overview:index') INDEX_URL = reverse('horizon:project:overview:index')
@ -38,19 +39,19 @@ INDEX_URL = reverse('horizon:project:overview:index')
class UsageViewTests(test.BaseAdminViewTests): class UsageViewTests(test.BaseAdminViewTests):
@test.create_stubs({api: ('usage_list',), @test.create_stubs({api: ('usage_list',),
api.nova: ('tenant_quota_usages',), quotas: ('tenant_quota_usages',),
api.keystone: ('tenant_list',)}) api.keystone: ('tenant_list',)})
def test_usage(self): def test_usage(self):
now = timezone.now() now = timezone.now()
usage_obj = api.nova.Usage(self.usages.first()) usage_obj = api.nova.NovaUsage(self.usages.first())
quotas = self.quota_usages.first() quota_data = self.quota_usages.first()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True) \ api.keystone.tenant_list(IsA(http.HttpRequest), admin=True) \
.AndReturn(self.tenants.list()) .AndReturn(self.tenants.list())
api.usage_list(IsA(http.HttpRequest), api.usage_list(IsA(http.HttpRequest),
datetime.datetime(now.year, now.month, 1, 0, 0, 0), datetime.datetime(now.year, now.month, 1, 0, 0, 0),
Func(usage.almost_now)) \ Func(usage.almost_now)) \
.AndReturn([usage_obj]) .AndReturn([usage_obj])
api.nova.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quotas) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data)
self.mox.ReplayAll() self.mox.ReplayAll()
res = self.client.get(reverse('horizon:admin:overview:index')) res = self.client.get(reverse('horizon:admin:overview:index'))
self.assertTemplateUsed(res, 'admin/overview/usage.html') self.assertTemplateUsed(res, 'admin/overview/usage.html')
@ -70,19 +71,19 @@ class UsageViewTests(test.BaseAdminViewTests):
usage_obj.total_local_gb_usage)) usage_obj.total_local_gb_usage))
@test.create_stubs({api: ('usage_list',), @test.create_stubs({api: ('usage_list',),
api.nova: ('tenant_quota_usages',), quotas: ('tenant_quota_usages',),
api.keystone: ('tenant_list',)}) api.keystone: ('tenant_list',)})
def test_usage_csv(self): def test_usage_csv(self):
now = timezone.now() now = timezone.now()
usage_obj = api.nova.Usage(self.usages.first()) usage_obj = api.nova.NovaUsage(self.usages.first())
quotas = self.quota_usages.first() quota_data = self.quota_usages.first()
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True) \ api.keystone.tenant_list(IsA(http.HttpRequest), admin=True) \
.AndReturn(self.tenants.list()) .AndReturn(self.tenants.list())
api.usage_list(IsA(http.HttpRequest), api.usage_list(IsA(http.HttpRequest),
datetime.datetime(now.year, now.month, 1, 0, 0, 0), datetime.datetime(now.year, now.month, 1, 0, 0, 0),
Func(usage.almost_now)) \ Func(usage.almost_now)) \
.AndReturn([usage_obj]) .AndReturn([usage_obj])
api.nova.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quotas) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data)
self.mox.ReplayAll() self.mox.ReplayAll()
csv_url = reverse('horizon:admin:overview:index') + "?format=csv" csv_url = reverse('horizon:admin:overview:index') + "?format=csv"
res = self.client.get(csv_url) res = self.client.get(csv_url)

View File

@ -21,6 +21,7 @@ from mox import IsA
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
from .workflows import CreateProject, UpdateProject from .workflows import CreateProject, UpdateProject
from .views import QUOTA_FIELDS from .views import QUOTA_FIELDS
@ -55,7 +56,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
def _get_quota_info(self, quota): def _get_quota_info(self, quota):
quota_data = {} quota_data = {}
for field in QUOTA_FIELDS: for field in QUOTA_FIELDS:
quota_data[field] = int(getattr(quota, field, None)) quota_data[field] = int(quota.get(field).limit)
return quota_data return quota_data
def _get_workflow_data(self, project, quota): def _get_workflow_data(self, project, quota):
@ -64,18 +65,17 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
project_info.update(quota_data) project_info.update(quota_data)
return project_info return project_info
@test.create_stubs({api: ('tenant_quota_defaults', @test.create_stubs({api: ('get_default_role',),
'get_default_role',), quotas: ('get_default_quota_data',),
api.keystone: ('user_list', api.keystone: ('user_list',
'role_list',)}) 'role_list',)})
def test_add_project_get(self): def test_add_project_get(self):
quota = self.quotas.first() quota = self.quotas.first()
default_role = self.roles.first() default_role = self.roles.first()
users = self.users.list() users = self.users.list()
roles = self.roles.list() roles = self.roles.list()
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
# init # init
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
@ -93,20 +93,21 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertEqual(res.context['workflow'].name, CreateProject.name) self.assertEqual(res.context['workflow'].name, CreateProject.name)
step = workflow.get_step("createprojectinfoaction") step = workflow.get_step("createprojectinfoaction")
self.assertEqual(step.action.initial['ram'], quota.ram) self.assertEqual(step.action.initial['ram'], quota.get('ram').limit)
self.assertEqual(step.action.initial['injected_files'], self.assertEqual(step.action.initial['injected_files'],
quota.injected_files) quota.get('injected_files').limit)
self.assertQuerysetEqual(workflow.steps, self.assertQuerysetEqual(workflow.steps,
['<CreateProjectInfo: createprojectinfoaction>', ['<CreateProjectInfo: createprojectinfoaction>',
'<UpdateProjectMembers: update_members>', '<UpdateProjectMembers: update_members>',
'<UpdateProjectQuota: update_quotas>']) '<UpdateProjectQuota: update_quotas>'])
@test.create_stubs({api: ('get_default_role', @test.create_stubs({api: ('get_default_role',
'tenant_quota_defaults',
'add_tenant_user_role',), 'add_tenant_user_role',),
api.keystone: ('tenant_create', api.keystone: ('tenant_create',
'user_list', 'user_list',
'role_list'), 'role_list'),
quotas: ('get_default_quota_data',),
api.cinder: ('tenant_quota_update',),
api.nova: ('tenant_quota_update',)}) api.nova: ('tenant_quota_update',)})
def test_add_project_post(self): def test_add_project_post(self):
project = self.tenants.first() project = self.tenants.first()
@ -116,8 +117,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
roles = self.roles.list() roles = self.roles.list()
# init # init
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -159,8 +159,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api: ('tenant_quota_defaults', @test.create_stubs({api: ('get_default_role',),
'get_default_role',), quotas: ('get_default_quota_data',),
api.keystone: ('user_list', api.keystone: ('user_list',
'role_list',)}) 'role_list',)})
def test_add_project_quota_defaults_error(self): def test_add_project_quota_defaults_error(self):
@ -169,8 +169,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
roles = self.roles.list() roles = self.roles.list()
# init # init
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_default_quota_data(IsA(http.HttpRequest)) \
.AndRaise(self.exceptions.nova) .AndRaise(self.exceptions.nova)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -184,8 +184,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertTemplateUsed(res, 'admin/projects/create.html') self.assertTemplateUsed(res, 'admin/projects/create.html')
self.assertContains(res, "Unable to retrieve default quota values") self.assertContains(res, "Unable to retrieve default quota values")
@test.create_stubs({api: ('get_default_role', @test.create_stubs({api: ('get_default_role',),
'tenant_quota_defaults',), quotas: ('get_default_quota_data',),
api.keystone: ('tenant_create', api.keystone: ('tenant_create',
'user_list', 'user_list',
'role_list',)}) 'role_list',)})
@ -197,8 +197,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
roles = self.roles.list() roles = self.roles.list()
# init # init
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -224,11 +223,11 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api: ('get_default_role', @test.create_stubs({api: ('get_default_role',
'tenant_quota_defaults',
'add_tenant_user_role',), 'add_tenant_user_role',),
api.keystone: ('tenant_create', api.keystone: ('tenant_create',
'user_list', 'user_list',
'role_list'), 'role_list'),
quotas: ('get_default_quota_data',),
api.nova: ('tenant_quota_update',)}) api.nova: ('tenant_quota_update',)})
def test_add_project_quota_update_error(self): def test_add_project_quota_update_error(self):
project = self.tenants.first() project = self.tenants.first()
@ -238,8 +237,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
roles = self.roles.list() roles = self.roles.list()
# init # init
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -283,11 +281,11 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api: ('get_default_role', @test.create_stubs({api: ('get_default_role',
'tenant_quota_defaults',
'add_tenant_user_role',), 'add_tenant_user_role',),
api.keystone: ('tenant_create', api.keystone: ('tenant_create',
'user_list', 'user_list',
'role_list',), 'role_list',),
quotas: ('get_default_quota_data',),
api.nova: ('tenant_quota_update',)}) api.nova: ('tenant_quota_update',)})
def test_add_project_user_update_error(self): def test_add_project_user_update_error(self):
project = self.tenants.first() project = self.tenants.first()
@ -297,8 +295,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
roles = self.roles.list() roles = self.roles.list()
# init # init
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -343,8 +340,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api: ('get_default_role', @test.create_stubs({api: ('get_default_role',),
'tenant_quota_defaults',), quotas: ('get_default_quota_data',),
api.keystone: ('user_list', api.keystone: ('user_list',
'role_list',)}) 'role_list',)})
def test_add_project_missing_field_error(self): def test_add_project_missing_field_error(self):
@ -355,8 +352,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
roles = self.roles.list() roles = self.roles.list()
# init # init
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -380,13 +376,13 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
def _get_quota_info(self, quota): def _get_quota_info(self, quota):
quota_data = {} quota_data = {}
for field in QUOTA_FIELDS: for field in QUOTA_FIELDS:
quota_data[field] = int(getattr(quota, field, None)) quota_data[field] = int(quota.get(field).limit)
return quota_data return quota_data
@test.create_stubs({api: ('get_default_role', @test.create_stubs({api: ('get_default_role',
'roles_for_user', 'roles_for_user',
'tenant_get', 'tenant_get',),
'tenant_quota_get',), quotas: ('get_tenant_quota_data',),
api.keystone: ('user_list', api.keystone: ('user_list',
'role_list',)}) 'role_list',)})
def test_update_project_get(self): def test_update_project_get(self):
@ -398,8 +394,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \ api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \
.AndReturn(project) .AndReturn(project)
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -422,9 +417,9 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertEqual(res.context['workflow'].name, UpdateProject.name) self.assertEqual(res.context['workflow'].name, UpdateProject.name)
step = workflow.get_step("update_info") step = workflow.get_step("update_info")
self.assertEqual(step.action.initial['ram'], quota.ram) self.assertEqual(step.action.initial['ram'], quota.get('ram').limit)
self.assertEqual(step.action.initial['injected_files'], self.assertEqual(step.action.initial['injected_files'],
quota.injected_files) quota.get('injected_files').limit)
self.assertEqual(step.action.initial['name'], project.name) self.assertEqual(step.action.initial['name'], project.name)
self.assertEqual(step.action.initial['description'], self.assertEqual(step.action.initial['description'],
project.description) project.description)
@ -434,13 +429,14 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
'<UpdateProjectQuota: update_quotas>']) '<UpdateProjectQuota: update_quotas>'])
@test.create_stubs({api: ('tenant_get', @test.create_stubs({api: ('tenant_get',
'tenant_quota_get',
'tenant_update', 'tenant_update',
'tenant_quota_update',
'get_default_role', 'get_default_role',
'roles_for_user', 'roles_for_user',
'remove_tenant_user_role', 'remove_tenant_user_role',
'add_tenant_user_role'), 'add_tenant_user_role'),
api.nova: ('tenant_quota_update',),
api.cinder: ('tenant_quota_update',),
quotas: ('get_tenant_quota_data',),
api.keystone: ('user_list', api.keystone: ('user_list',
'role_list',)}) 'role_list',)})
def test_update_project_save(self): def test_update_project_save(self):
@ -453,8 +449,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
# get/init # get/init
api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \ api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \
.AndReturn(project) .AndReturn(project)
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -524,9 +519,13 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
user_id='3', user_id='3',
role_id='1') role_id='1')
api.tenant_quota_update(IsA(http.HttpRequest), api.nova.tenant_quota_update(IsA(http.HttpRequest),
project.id, project.id,
**updated_quota) **updated_quota)
api.cinder.tenant_quota_update(IsA(http.HttpRequest),
project.id,
volumes=updated_quota['volumes'],
gigabytes=updated_quota['gigabytes'])
self.mox.ReplayAll() self.mox.ReplayAll()
# submit form data # submit form data
@ -559,13 +558,13 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api: ('tenant_get', @test.create_stubs({api: ('tenant_get',
'tenant_quota_get',
'tenant_update', 'tenant_update',
'tenant_quota_update',
'get_default_role', 'get_default_role',
'roles_for_user', 'roles_for_user',
'remove_tenant_user', 'remove_tenant_user',
'add_tenant_user_role'), 'add_tenant_user_role'),
quotas: ('get_tenant_quota_data',),
api.nova: ('tenant_quota_update',),
api.keystone: ('user_list', api.keystone: ('user_list',
'role_list',)}) 'role_list',)})
def test_update_project_tenant_update_error(self): def test_update_project_tenant_update_error(self):
@ -578,8 +577,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
# get/init # get/init
api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \ api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \
.AndReturn(project) .AndReturn(project)
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -631,13 +629,13 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api: ('tenant_get', @test.create_stubs({api: ('tenant_get',
'tenant_quota_get',
'tenant_update', 'tenant_update',
'tenant_quota_update',
'get_default_role', 'get_default_role',
'roles_for_user', 'roles_for_user',
'remove_tenant_user_role', 'remove_tenant_user_role',
'add_tenant_user_role'), 'add_tenant_user_role'),
quotas: ('get_tenant_quota_data',),
api.nova: ('tenant_quota_update',),
api.keystone: ('user_list', api.keystone: ('user_list',
'role_list',)}) 'role_list',)})
def test_update_project_quota_update_error(self): def test_update_project_quota_update_error(self):
@ -650,8 +648,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
# get/init # get/init
api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \ api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \
.AndReturn(project) .AndReturn(project)
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
@ -708,7 +705,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
user_id='3', user_id='3',
role_id='2') role_id='2')
api.tenant_quota_update(IsA(http.HttpRequest), api.nova.tenant_quota_update(IsA(http.HttpRequest),
project.id, project.id,
**updated_quota).AndRaise(self.exceptions.nova) **updated_quota).AndRaise(self.exceptions.nova)
@ -730,12 +727,12 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api: ('tenant_get', @test.create_stubs({api: ('tenant_get',
'tenant_quota_get',
'tenant_update', 'tenant_update',
'get_default_role', 'get_default_role',
'roles_for_user', 'roles_for_user',
'remove_tenant_user_role', 'remove_tenant_user_role',
'add_tenant_user_role'), 'add_tenant_user_role'),
quotas: ('get_tenant_quota_data',),
api.keystone: ('user_list', api.keystone: ('user_list',
'role_list',)}) 'role_list',)})
def test_update_project_member_update_error(self): def test_update_project_member_update_error(self):
@ -748,8 +745,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
# get/init # get/init
api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \ api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \
.AndReturn(project) .AndReturn(project)
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \ quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
.AndReturn(quota)
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role) api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users) api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)

View File

@ -29,6 +29,7 @@ from horizon import workflows
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard import usage from openstack_dashboard import usage
from openstack_dashboard.usage import quotas
from openstack_dashboard.dashboards.admin.users.views import CreateView from openstack_dashboard.dashboards.admin.users.views import CreateView
from .forms import CreateUser from .forms import CreateUser
from .tables import TenantsTable, TenantUsersTable, AddUsersTable from .tables import TenantsTable, TenantUsersTable, AddUsersTable
@ -145,10 +146,9 @@ class CreateProjectView(workflows.WorkflowView):
# get initial quota defaults # get initial quota defaults
try: try:
quota_defaults = api.tenant_quota_defaults(self.request, quota_defaults = quotas.get_default_quota_data(self.request)
self.request.user.tenant_id)
for field in QUOTA_FIELDS: for field in QUOTA_FIELDS:
initial[field] = getattr(quota_defaults, field, None) initial[field] = quota_defaults.get(field).limit
except: except:
error_msg = _('Unable to retrieve default quota values.') error_msg = _('Unable to retrieve default quota values.')
@ -174,9 +174,9 @@ class UpdateProjectView(workflows.WorkflowView):
initial[field] = getattr(project_info, field, None) initial[field] = getattr(project_info, field, None)
# get initial project quota # get initial project quota
quota_data = api.tenant_quota_get(self.request, project_id) quota_data = quotas.get_tenant_quota_data(self.request)
for field in QUOTA_FIELDS: for field in QUOTA_FIELDS:
initial[field] = getattr(quota_data, field, None) initial[field] = quota_data.get(field).limit
except: except:
exceptions.handle(self.request, exceptions.handle(self.request,
_('Unable to retrieve project details.'), _('Unable to retrieve project details.'),

View File

@ -28,6 +28,8 @@ from horizon import forms
from horizon import messages from horizon import messages
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.api import cinder, nova
from openstack_dashboard.api.base import is_service_enabled
INDEX_URL = "horizon:admin:projects:index" INDEX_URL = "horizon:admin:projects:index"
@ -361,17 +363,25 @@ class UpdateProject(workflows.Workflow):
# update the project quota # update the project quota
ifcb = data['injected_file_content_bytes'] ifcb = data['injected_file_content_bytes']
try: try:
api.tenant_quota_update(request, # TODO(gabriel): Once nova-volume is fully deprecated the
project_id, # "volumes" and "gigabytes" quotas should no longer be sent to
metadata_items=data['metadata_items'], # the nova API to be updated anymore.
injected_file_content_bytes=ifcb, nova.tenant_quota_update(request,
volumes=data['volumes'], project_id,
gigabytes=data['gigabytes'], metadata_items=data['metadata_items'],
ram=data['ram'], injected_file_content_bytes=ifcb,
floating_ips=data['floating_ips'], volumes=data['volumes'],
instances=data['instances'], gigabytes=data['gigabytes'],
injected_files=data['injected_files'], ram=data['ram'],
cores=data['cores']) floating_ips=data['floating_ips'],
instances=data['instances'],
injected_files=data['injected_files'],
cores=data['cores'])
if is_service_enabled(request, 'volume'):
cinder.tenant_quota_update(request,
project_id,
volumes=data['volumes'],
gigabytes=data['gigabytes'])
return True return True
except: except:
exceptions.handle(request, _('Modified project information and ' exceptions.handle(request, _('Modified project information and '

View File

@ -38,7 +38,7 @@ class IndexView(tables.DataTableView):
def get_data(self): def get_data(self):
try: try:
quota_set = api.tenant_quota_defaults(self.request, quota_set = api.default_quota_get(self.request,
self.request.user.tenant_id) self.request.user.tenant_id)
data = quota_set.items data = quota_set.items
except: except:

View File

@ -31,6 +31,7 @@ from horizon import forms
from horizon import workflows from horizon import workflows
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.usage import quotas
from .forms import FloatingIpAllocate from .forms import FloatingIpAllocate
from .workflows import IPAssociationWorkflow from .workflows import IPAssociationWorkflow
@ -51,7 +52,7 @@ class AllocateView(forms.ModalFormView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(AllocateView, self).get_context_data(**kwargs) context = super(AllocateView, self).get_context_data(**kwargs)
try: try:
context['usages'] = api.tenant_quota_usages(self.request) context['usages'] = quotas.tenant_quota_usages(self.request)
except: except:
exceptions.handle(self.request) exceptions.handle(self.request)
return context return context

View File

@ -24,6 +24,7 @@ from django.utils.translation import ugettext_lazy as _
from horizon import tables from horizon import tables
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.api import cinder
from ...volumes import tables as volume_tables from ...volumes import tables as volume_tables
@ -57,7 +58,7 @@ class UpdateRow(tables.Row):
ajax = True ajax = True
def get_data(self, request, snapshot_id): def get_data(self, request, snapshot_id):
snapshot = api.nova.volume_snapshot_get(request, snapshot_id) snapshot = cinder.volume_snapshot_get(request, snapshot_id)
return snapshot return snapshot

View File

@ -20,7 +20,7 @@ from django.utils.translation import ugettext_lazy as _
from horizon import exceptions from horizon import exceptions
from horizon import tabs from horizon import tabs
from openstack_dashboard import api from openstack_dashboard.api import cinder
class OverviewTab(tabs.Tab): class OverviewTab(tabs.Tab):
@ -32,8 +32,8 @@ class OverviewTab(tabs.Tab):
def get_context_data(self, request): def get_context_data(self, request):
snapshot_id = self.tab_group.kwargs['snapshot_id'] snapshot_id = self.tab_group.kwargs['snapshot_id']
try: try:
snapshot = api.nova.volume_snapshot_get(request, snapshot_id) snapshot = cinder.volume_snapshot_get(request, snapshot_id)
volume = api.nova.volume_get(request, snapshot.volume_id) volume = cinder.volume_get(request, snapshot.volume_id)
volume.display_name = None volume.display_name = None
except: except:
redirect = reverse('horizon:project:images_and_snapshots:index') redirect = reverse('horizon:project:images_and_snapshots:index')

View File

@ -26,7 +26,9 @@ from django.utils.datastructures import SortedDict
from mox import IsA, IgnoreArg from mox import IsA, IgnoreArg
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.api import cinder
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
from .tabs import InstanceDetailTabs from .tabs import InstanceDetailTabs
from .workflows import LaunchInstance from .workflows import LaunchInstance
@ -324,7 +326,7 @@ class InstanceTests(test.TestCase):
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api: ("server_get", @test.create_stubs({api: ("server_get",
"volume_instance_list", "instance_volumes_list",
"flavor_get", "flavor_get",
"server_security_groups")}) "server_security_groups")})
def test_instance_details_volumes(self): def test_instance_details_volumes(self):
@ -332,8 +334,8 @@ class InstanceTests(test.TestCase):
volumes = [self.volumes.list()[1]] volumes = [self.volumes.list()[1]]
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.volume_instance_list(IsA(http.HttpRequest), api.instance_volumes_list(IsA(http.HttpRequest),
server.id).AndReturn(volumes) server.id).AndReturn(volumes)
api.flavor_get(IsA(http.HttpRequest), api.flavor_get(IsA(http.HttpRequest),
server.flavor['id']).AndReturn(self.flavors.first()) server.flavor['id']).AndReturn(self.flavors.first())
api.server_security_groups(IsA(http.HttpRequest), api.server_security_groups(IsA(http.HttpRequest),
@ -348,7 +350,7 @@ class InstanceTests(test.TestCase):
self.assertItemsEqual(res.context['instance'].volumes, volumes) self.assertItemsEqual(res.context['instance'].volumes, volumes)
@test.create_stubs({api: ("server_get", @test.create_stubs({api: ("server_get",
"volume_instance_list", "instance_volumes_list",
"flavor_get", "flavor_get",
"server_security_groups")}) "server_security_groups")})
def test_instance_details_volume_sorting(self): def test_instance_details_volume_sorting(self):
@ -356,8 +358,8 @@ class InstanceTests(test.TestCase):
volumes = self.volumes.list()[1:3] volumes = self.volumes.list()[1:3]
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.volume_instance_list(IsA(http.HttpRequest), api.instance_volumes_list(IsA(http.HttpRequest),
server.id).AndReturn(volumes) server.id).AndReturn(volumes)
api.flavor_get(IsA(http.HttpRequest), api.flavor_get(IsA(http.HttpRequest),
server.flavor['id']).AndReturn(self.flavors.first()) server.flavor['id']).AndReturn(self.flavors.first())
api.server_security_groups(IsA(http.HttpRequest), api.server_security_groups(IsA(http.HttpRequest),
@ -376,15 +378,15 @@ class InstanceTests(test.TestCase):
"/dev/hdk") "/dev/hdk")
@test.create_stubs({api: ("server_get", @test.create_stubs({api: ("server_get",
"volume_instance_list", "instance_volumes_list",
"flavor_get", "flavor_get",
"server_security_groups",)}) "server_security_groups",)})
def test_instance_details_metadata(self): def test_instance_details_metadata(self):
server = self.servers.first() server = self.servers.first()
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.volume_instance_list(IsA(http.HttpRequest), api.instance_volumes_list(IsA(http.HttpRequest),
server.id).AndReturn([]) server.id).AndReturn([])
api.flavor_get(IsA(http.HttpRequest), api.flavor_get(IsA(http.HttpRequest),
server.flavor['id']).AndReturn(self.flavors.first()) server.flavor['id']).AndReturn(self.flavors.first())
api.server_security_groups(IsA(http.HttpRequest), api.server_security_groups(IsA(http.HttpRequest),
@ -582,30 +584,30 @@ class InstanceTests(test.TestCase):
self.assertRedirectsNoFollow(res, INDEX_URL) self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.nova: ('tenant_quota_usages', @test.create_stubs({api.nova: ('flavor_list',
'flavor_list',
'keypair_list', 'keypair_list',
'security_group_list', 'security_group_list',),
'volume_snapshot_list', cinder: ('volume_snapshot_list',
'volume_list',), 'volume_list',),
quotas: ('tenant_quota_usages',),
api.quantum: ('network_list',), api.quantum: ('network_list',),
api.glance: ('image_list_detailed',)}) api.glance: ('image_list_detailed',)})
def test_launch_instance_get(self): def test_launch_instance_get(self):
quota_usages = self.quota_usages.first() quota_usages = self.quota_usages.first()
image = self.images.first() image = self.images.first()
api.nova.volume_list(IsA(http.HttpRequest)) \ cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list()) .AndReturn(self.volumes.list())
api.nova.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
api.glance.image_list_detailed(IsA(http.HttpRequest), api.glance.image_list_detailed(IsA(http.HttpRequest),
filters={'is_public': True, filters={'is_public': True,
'status': 'active'}) \ 'status': 'active'}) \
.AndReturn([self.images.list(), False]) .AndReturn([self.images.list(), False])
api.glance.image_list_detailed(IsA(http.HttpRequest), api.glance.image_list_detailed(IsA(http.HttpRequest),
filters={'property-owner_id': self.tenant.id, filters={'property-owner_id': self.tenant.id,
'status': 'active'}) \ 'status': 'active'}) \
.AndReturn([[], False]) .AndReturn([[], False])
api.quantum.network_list(IsA(http.HttpRequest), api.quantum.network_list(IsA(http.HttpRequest),
tenant_id=self.tenant.id, tenant_id=self.tenant.id,
shared=False) \ shared=False) \
@ -613,7 +615,7 @@ class InstanceTests(test.TestCase):
api.quantum.network_list(IsA(http.HttpRequest), api.quantum.network_list(IsA(http.HttpRequest),
shared=True) \ shared=True) \
.AndReturn(self.networks.list()[1:]) .AndReturn(self.networks.list()[1:])
api.nova.tenant_quota_usages(IsA(http.HttpRequest)) \ quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(quota_usages) .AndReturn(quota_usages)
api.nova.flavor_list(IsA(http.HttpRequest)) \ api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list()) .AndReturn(self.flavors.list())
@ -646,13 +648,13 @@ class InstanceTests(test.TestCase):
@test.create_stubs({api.glance: ('image_list_detailed',), @test.create_stubs({api.glance: ('image_list_detailed',),
api.quantum: ('network_list',), api.quantum: ('network_list',),
quotas: ('tenant_quota_usages',),
api.nova: ('flavor_list', api.nova: ('flavor_list',
'keypair_list', 'keypair_list',
'security_group_list', 'security_group_list',
'volume_list', 'server_create',),
'volume_snapshot_list', cinder: ('volume_list',
'tenant_quota_usages', 'volume_snapshot_list',)})
'server_create',)})
def test_launch_instance_post(self): def test_launch_instance_post(self):
flavor = self.flavors.first() flavor = self.flavors.first()
image = self.images.first() image = self.images.first()
@ -687,9 +689,9 @@ class InstanceTests(test.TestCase):
api.quantum.network_list(IsA(http.HttpRequest), api.quantum.network_list(IsA(http.HttpRequest),
shared=True) \ shared=True) \
.AndReturn(self.networks.list()[1:]) .AndReturn(self.networks.list()[1:])
api.nova.volume_list(IsA(http.HttpRequest)) \ cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list()) .AndReturn(self.volumes.list())
api.nova.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([]) cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
api.nova.server_create(IsA(http.HttpRequest), api.nova.server_create(IsA(http.HttpRequest),
server.name, server.name,
image.id, image.id,
@ -725,12 +727,12 @@ class InstanceTests(test.TestCase):
@test.create_stubs({api.glance: ('image_list_detailed',), @test.create_stubs({api.glance: ('image_list_detailed',),
api.quantum: ('network_list',), api.quantum: ('network_list',),
api.nova: ('flavor_list', quotas: ('tenant_quota_usages',),
'keypair_list', api.nova: ('flavor_list',
'security_group_list', 'keypair_list',
'volume_list', 'security_group_list',),
'tenant_quota_usages', cinder: ('volume_list',
'volume_snapshot_list',)}) 'volume_snapshot_list',)})
def test_launch_instance_post_no_images_available(self): def test_launch_instance_post_no_images_available(self):
flavor = self.flavors.first() flavor = self.flavors.first()
keypair = self.keypairs.first() keypair = self.keypairs.first()
@ -743,7 +745,7 @@ class InstanceTests(test.TestCase):
api.nova.flavor_list(IsA(http.HttpRequest)) \ api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list()) .AndReturn(self.flavors.list())
api.nova.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn({}) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn({})
api.glance.image_list_detailed(IsA(http.HttpRequest), api.glance.image_list_detailed(IsA(http.HttpRequest),
filters={'is_public': True, filters={'is_public': True,
'status': 'active'}) \ 'status': 'active'}) \
@ -765,9 +767,9 @@ class InstanceTests(test.TestCase):
.AndReturn(self.keypairs.list()) .AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \ api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list()) .AndReturn(self.security_groups.list())
api.nova.volume_list(IsA(http.HttpRequest)) \ cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list()) .AndReturn(self.volumes.list())
api.nova.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([]) cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
self.mox.ReplayAll() self.mox.ReplayAll()
@ -795,16 +797,16 @@ class InstanceTests(test.TestCase):
@test.create_stubs({api.glance: ('image_list_detailed',), @test.create_stubs({api.glance: ('image_list_detailed',),
api.quantum: ('network_list',), api.quantum: ('network_list',),
api.nova: ('tenant_quota_usages', quotas: ('tenant_quota_usages',),
'flavor_list', cinder: ('volume_list',
'volume_snapshot_list',),
api.nova: ('flavor_list',
'keypair_list', 'keypair_list',
'volume_list', 'security_group_list',)})
'security_group_list',
'volume_snapshot_list',)})
def test_launch_flavorlist_error(self): def test_launch_flavorlist_error(self):
api.nova.volume_list(IsA(http.HttpRequest)) \ cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list()) .AndReturn(self.volumes.list())
api.nova.volume_snapshot_list(IsA(http.HttpRequest)) \ cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list()) .AndReturn(self.volumes.list())
api.glance.image_list_detailed(IsA(http.HttpRequest), api.glance.image_list_detailed(IsA(http.HttpRequest),
filters={'is_public': True, filters={'is_public': True,
@ -821,7 +823,7 @@ class InstanceTests(test.TestCase):
api.quantum.network_list(IsA(http.HttpRequest), api.quantum.network_list(IsA(http.HttpRequest),
shared=True) \ shared=True) \
.AndReturn(self.networks.list()[1:]) .AndReturn(self.networks.list()[1:])
api.nova.tenant_quota_usages(IsA(http.HttpRequest)) \ quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(self.quota_usages.first()) .AndReturn(self.quota_usages.first())
api.nova.flavor_list(IsA(http.HttpRequest)) \ api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndRaise(self.exceptions.nova) .AndRaise(self.exceptions.nova)
@ -845,9 +847,9 @@ class InstanceTests(test.TestCase):
api.nova: ('flavor_list', api.nova: ('flavor_list',
'keypair_list', 'keypair_list',
'security_group_list', 'security_group_list',
'volume_list', 'server_create',),
'server_create', cinder: ('volume_list',
'volume_snapshot_list',)}) 'volume_snapshot_list',)})
def test_launch_form_keystone_exception(self): def test_launch_form_keystone_exception(self):
flavor = self.flavors.first() flavor = self.flavors.first()
image = self.images.first() image = self.images.first()
@ -857,8 +859,8 @@ class InstanceTests(test.TestCase):
customization_script = 'userData' customization_script = 'userData'
nics = [{"net-id": self.networks.first().id, "v4-fixed-ip": ''}] nics = [{"net-id": self.networks.first().id, "v4-fixed-ip": ''}]
api.nova.volume_snapshot_list(IsA(http.HttpRequest)) \ cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list()) .AndReturn(self.volumes.list())
api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
api.nova.keypair_list(IgnoreArg()).AndReturn(self.keypairs.list()) api.nova.keypair_list(IgnoreArg()).AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \ api.nova.security_group_list(IsA(http.HttpRequest)) \
@ -878,7 +880,7 @@ class InstanceTests(test.TestCase):
api.quantum.network_list(IsA(http.HttpRequest), api.quantum.network_list(IsA(http.HttpRequest),
shared=True) \ shared=True) \
.AndReturn(self.networks.list()[1:]) .AndReturn(self.networks.list()[1:])
api.nova.volume_list(IgnoreArg()).AndReturn(self.volumes.list()) cinder.volume_list(IgnoreArg()).AndReturn(self.volumes.list())
api.nova.server_create(IsA(http.HttpRequest), api.nova.server_create(IsA(http.HttpRequest),
server.name, server.name,
image.id, image.id,
@ -912,12 +914,12 @@ class InstanceTests(test.TestCase):
@test.create_stubs({api.glance: ('image_list_detailed',), @test.create_stubs({api.glance: ('image_list_detailed',),
api.quantum: ('network_list',), api.quantum: ('network_list',),
quotas: ('tenant_quota_usages',),
api.nova: ('flavor_list', api.nova: ('flavor_list',
'keypair_list', 'keypair_list',
'security_group_list', 'security_group_list',),
'volume_list', cinder: ('volume_list',
'tenant_quota_usages', 'volume_snapshot_list',)})
'volume_snapshot_list',)})
def test_launch_form_instance_count_error(self): def test_launch_form_instance_count_error(self):
flavor = self.flavors.first() flavor = self.flavors.first()
image = self.images.first() image = self.images.first()
@ -950,13 +952,13 @@ class InstanceTests(test.TestCase):
api.quantum.network_list(IsA(http.HttpRequest), api.quantum.network_list(IsA(http.HttpRequest),
shared=True) \ shared=True) \
.AndReturn(self.networks.list()[1:]) .AndReturn(self.networks.list()[1:])
api.nova.volume_list(IsA(http.HttpRequest)) \ cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list()) .AndReturn(self.volumes.list())
api.nova.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([]) cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
api.nova.flavor_list(IsA(http.HttpRequest)) \ api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list()) .AndReturn(self.flavors.list())
api.nova.tenant_quota_usages(IsA(http.HttpRequest)) \ quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(self.quota_usages.first()) .AndReturn(self.quota_usages.first())
self.mox.ReplayAll() self.mox.ReplayAll()

View File

@ -165,8 +165,8 @@ class DetailView(tabs.TabView):
try: try:
instance_id = self.kwargs['instance_id'] instance_id = self.kwargs['instance_id']
instance = api.server_get(self.request, instance_id) instance = api.server_get(self.request, instance_id)
instance.volumes = api.volume_instance_list(self.request, instance.volumes = api.instance_volumes_list(self.request,
instance_id) instance_id)
# Sort by device name # Sort by device name
instance.volumes.sort(key=lambda vol: vol.device) instance.volumes.sort(key=lambda vol: vol.device)
instance.full_flavor = api.flavor_get(self.request, instance.full_flavor = api.flavor_get(self.request,

View File

@ -29,6 +29,8 @@ from horizon import forms
from horizon import workflows from horizon import workflows
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.api import cinder
from openstack_dashboard.usage import quotas
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -116,7 +118,7 @@ class VolumeOptionsAction(workflows.Action):
def populate_volume_id_choices(self, request, context): def populate_volume_id_choices(self, request, context):
volume_options = [("", _("Select Volume"))] volume_options = [("", _("Select Volume"))]
try: try:
volumes = [v for v in api.nova.volume_list(self.request) volumes = [v for v in cinder.volume_list(self.request)
if v.status == api.VOLUME_STATE_AVAILABLE] if v.status == api.VOLUME_STATE_AVAILABLE]
volume_options.extend([self._get_volume_display_name(vol) volume_options.extend([self._get_volume_display_name(vol)
for vol in volumes]) for vol in volumes])
@ -128,7 +130,7 @@ class VolumeOptionsAction(workflows.Action):
def populate_volume_snapshot_id_choices(self, request, context): def populate_volume_snapshot_id_choices(self, request, context):
volume_options = [("", _("Select Volume Snapshot"))] volume_options = [("", _("Select Volume Snapshot"))]
try: try:
snapshots = api.nova.volume_snapshot_list(self.request) snapshots = cinder.volume_snapshot_list(self.request)
snapshots = [s for s in snapshots snapshots = [s for s in snapshots
if s.status == api.VOLUME_STATE_AVAILABLE] if s.status == api.VOLUME_STATE_AVAILABLE]
volume_options.extend([self._get_volume_display_name(snap) volume_options.extend([self._get_volume_display_name(snap)
@ -294,7 +296,7 @@ class SetInstanceDetailsAction(workflows.Action):
def get_help_text(self): def get_help_text(self):
extra = {} extra = {}
try: try:
extra['usages'] = api.nova.tenant_quota_usages(self.request) extra['usages'] = quotas.tenant_quota_usages(self.request)
extra['usages_json'] = json.dumps(extra['usages']) extra['usages_json'] = json.dumps(extra['usages'])
flavors = json.dumps([f._info for f in flavors = json.dumps([f._info for f in
api.nova.flavor_list(self.request)]) api.nova.flavor_list(self.request)])

View File

@ -27,8 +27,9 @@ from django.utils import timezone
from mox import IsA, Func from mox import IsA, Func
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.test import helpers as test
from openstack_dashboard import usage from openstack_dashboard import usage
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
INDEX_URL = reverse('horizon:project:overview:index') INDEX_URL = reverse('horizon:project:overview:index')
@ -37,15 +38,15 @@ INDEX_URL = reverse('horizon:project:overview:index')
class UsageViewTests(test.TestCase): class UsageViewTests(test.TestCase):
def test_usage(self): def test_usage(self):
now = timezone.now() now = timezone.now()
usage_obj = api.nova.Usage(self.usages.first()) usage_obj = api.nova.NovaUsage(self.usages.first())
quotas = self.quota_usages.first() quota_data = self.quota_usages.first()
self.mox.StubOutWithMock(api, 'usage_get') self.mox.StubOutWithMock(api, 'usage_get')
self.mox.StubOutWithMock(api.nova, 'tenant_quota_usages') self.mox.StubOutWithMock(quotas, 'tenant_quota_usages')
api.usage_get(IsA(http.HttpRequest), self.tenant.id, api.usage_get(IsA(http.HttpRequest), self.tenant.id,
datetime.datetime(now.year, now.month, 1, 0, 0, 0), datetime.datetime(now.year, now.month, 1, 0, 0, 0),
Func(usage.almost_now)) \ Func(usage.almost_now)) \
.AndReturn(usage_obj) .AndReturn(usage_obj)
api.nova.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quotas) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data)
self.mox.ReplayAll() self.mox.ReplayAll()
res = self.client.get(reverse('horizon:project:overview:index')) res = self.client.get(reverse('horizon:project:overview:index'))
@ -69,17 +70,17 @@ class UsageViewTests(test.TestCase):
def test_usage_csv(self): def test_usage_csv(self):
now = timezone.now() now = timezone.now()
usage_obj = api.nova.Usage(self.usages.first()) usage_obj = api.nova.NovaUsage(self.usages.first())
quotas = self.quota_usages.first() quota_data = self.quota_usages.first()
self.mox.StubOutWithMock(api, 'usage_get') self.mox.StubOutWithMock(api, 'usage_get')
self.mox.StubOutWithMock(api.nova, 'tenant_quota_usages') self.mox.StubOutWithMock(quotas, 'tenant_quota_usages')
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
api.usage_get(IsA(http.HttpRequest), api.usage_get(IsA(http.HttpRequest),
self.tenant.id, self.tenant.id,
timestamp, timestamp,
Func(usage.almost_now)) \ Func(usage.almost_now)) \
.AndReturn(usage_obj) .AndReturn(usage_obj)
api.nova.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quotas) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data)
self.mox.ReplayAll() self.mox.ReplayAll()
res = self.client.get(reverse('horizon:project:overview:index') + res = self.client.get(reverse('horizon:project:overview:index') +
@ -89,16 +90,16 @@ class UsageViewTests(test.TestCase):
def test_usage_exception_usage(self): def test_usage_exception_usage(self):
now = timezone.now() now = timezone.now()
quotas = self.quota_usages.first() quota_data = self.quota_usages.first()
self.mox.StubOutWithMock(api, 'usage_get') self.mox.StubOutWithMock(api, 'usage_get')
self.mox.StubOutWithMock(api.nova, 'tenant_quota_usages') self.mox.StubOutWithMock(quotas, 'tenant_quota_usages')
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
api.usage_get(IsA(http.HttpRequest), api.usage_get(IsA(http.HttpRequest),
self.tenant.id, self.tenant.id,
timestamp, timestamp,
Func(usage.almost_now)) \ Func(usage.almost_now)) \
.AndRaise(self.exceptions.nova) .AndRaise(self.exceptions.nova)
api.nova.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quotas) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data)
self.mox.ReplayAll() self.mox.ReplayAll()
res = self.client.get(reverse('horizon:project:overview:index')) res = self.client.get(reverse('horizon:project:overview:index'))
@ -107,16 +108,16 @@ class UsageViewTests(test.TestCase):
def test_usage_exception_quota(self): def test_usage_exception_quota(self):
now = timezone.now() now = timezone.now()
usage_obj = api.nova.Usage(self.usages.first()) usage_obj = api.nova.NovaUsage(self.usages.first())
self.mox.StubOutWithMock(api, 'usage_get') self.mox.StubOutWithMock(api, 'usage_get')
self.mox.StubOutWithMock(api.nova, 'tenant_quota_usages') self.mox.StubOutWithMock(quotas, 'tenant_quota_usages')
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
api.usage_get(IsA(http.HttpRequest), api.usage_get(IsA(http.HttpRequest),
self.tenant.id, self.tenant.id,
timestamp, timestamp,
Func(usage.almost_now)) \ Func(usage.almost_now)) \
.AndReturn(usage_obj) .AndReturn(usage_obj)
api.nova.tenant_quota_usages(IsA(http.HttpRequest))\ quotas.tenant_quota_usages(IsA(http.HttpRequest))\
.AndRaise(self.exceptions.nova) .AndRaise(self.exceptions.nova)
self.mox.ReplayAll() self.mox.ReplayAll()
@ -126,17 +127,17 @@ class UsageViewTests(test.TestCase):
def test_usage_default_tenant(self): def test_usage_default_tenant(self):
now = timezone.now() now = timezone.now()
usage_obj = api.nova.Usage(self.usages.first()) usage_obj = api.nova.NovaUsage(self.usages.first())
quotas = self.quota_usages.first() quota_data = self.quota_usages.first()
self.mox.StubOutWithMock(api, 'usage_get') self.mox.StubOutWithMock(api, 'usage_get')
self.mox.StubOutWithMock(api.nova, 'tenant_quota_usages') self.mox.StubOutWithMock(quotas, 'tenant_quota_usages')
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
api.usage_get(IsA(http.HttpRequest), api.usage_get(IsA(http.HttpRequest),
self.tenant.id, self.tenant.id,
timestamp, timestamp,
Func(usage.almost_now)) \ Func(usage.almost_now)) \
.AndReturn(usage_obj) .AndReturn(usage_obj)
api.nova.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quotas) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data)
self.mox.ReplayAll() self.mox.ReplayAll()
res = self.client.get(reverse('horizon:project:overview:index')) res = self.client.get(reverse('horizon:project:overview:index'))

View File

@ -19,6 +19,8 @@ from horizon.utils.fields import SelectWidget
from horizon.utils.memoized import memoized from horizon.utils.memoized import memoized
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.api import cinder
from openstack_dashboard.usage import quotas
from ..instances.tables import ACTIVE_STATES from ..instances.tables import ACTIVE_STATES
@ -71,7 +73,7 @@ class CreateForm(forms.SelfHandlingForm):
# error message when the quota is exceeded when trying to create # error message when the quota is exceeded when trying to create
# a volume, so we need to check for that scenario here before we # a volume, so we need to check for that scenario here before we
# send it off to try and create. # send it off to try and create.
usages = api.tenant_quota_usages(request) usages = quotas.tenant_quota_usages(request)
snapshot_id = None snapshot_id = None
if (data.get("snapshot_source", None)): if (data.get("snapshot_source", None)):
@ -116,7 +118,7 @@ class CreateForm(forms.SelfHandlingForm):
@memoized @memoized
def get_snapshot(self, request, id): def get_snapshot(self, request, id):
return api.nova.volume_snapshot_get(request, id) return cinder.volume_snapshot_get(request, id)
class AttachForm(forms.SelfHandlingForm): class AttachForm(forms.SelfHandlingForm):
@ -170,10 +172,10 @@ class AttachForm(forms.SelfHandlingForm):
# it, so let's slice that off... # it, so let's slice that off...
instance_name = instance_name.rsplit(" (")[0] instance_name = instance_name.rsplit(" (")[0]
try: try:
vol = api.volume_attach(request, vol = api.instance_volume_attach(request,
data['volume_id'], data['volume_id'],
data['instance'], data['instance'],
data.get('device', '')) data.get('device', ''))
vol_name = api.volume_get(request, data['volume_id']).display_name vol_name = api.volume_get(request, data['volume_id']).display_name
message = _('Attaching volume %(vol)s to instance ' message = _('Attaching volume %(vol)s to instance '

View File

@ -185,7 +185,9 @@ class DetachVolume(tables.BatchAction):
def action(self, request, obj_id): def action(self, request, obj_id):
attachment = self.table.get_object_by_id(obj_id) attachment = self.table.get_object_by_id(obj_id)
api.volume_detach(request, attachment.get('server_id', None), obj_id) api.instance_volume_detach(request,
attachment.get('server_id', None),
obj_id)
def get_success_url(self, request): def get_success_url(self, request):
return reverse('horizon:project:volumes:index') return reverse('horizon:project:volumes:index')

View File

@ -20,7 +20,7 @@ from django.utils.translation import ugettext_lazy as _
from horizon import exceptions from horizon import exceptions
from horizon import tabs from horizon import tabs
from openstack_dashboard import api from openstack_dashboard.api import cinder, nova
class OverviewTab(tabs.Tab): class OverviewTab(tabs.Tab):
@ -32,10 +32,9 @@ class OverviewTab(tabs.Tab):
def get_context_data(self, request): def get_context_data(self, request):
volume_id = self.tab_group.kwargs['volume_id'] volume_id = self.tab_group.kwargs['volume_id']
try: try:
volume = api.nova.volume_get(request, volume_id) volume = cinder.volume_get(request, volume_id)
for att in volume.attachments: for att in volume.attachments:
att['instance'] = api.nova.server_get(request, att['instance'] = nova.server_get(request, att['server_id'])
att['server_id'])
except: except:
redirect = reverse('horizon:project:volumes:index') redirect = reverse('horizon:project:volumes:index')
exceptions.handle(self.request, exceptions.handle(self.request,

View File

@ -26,12 +26,15 @@ from django.forms import widgets
from mox import IsA from mox import IsA
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.api import cinder
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
class VolumeViewTests(test.TestCase): class VolumeViewTests(test.TestCase):
@test.create_stubs({api: ('tenant_quota_usages', 'volume_create', @test.create_stubs({api: ('volume_create',
'volume_snapshot_list')}) 'volume_snapshot_list'),
quotas: ('tenant_quota_usages',)})
def test_create_volume(self): def test_create_volume(self):
volume = self.volumes.first() volume = self.volumes.first()
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
@ -40,7 +43,7 @@ class VolumeViewTests(test.TestCase):
'method': u'CreateForm', 'method': u'CreateForm',
'size': 50, 'snapshot_source': ''} 'size': 50, 'snapshot_source': ''}
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
api.volume_snapshot_list(IsA(http.HttpRequest)).\ api.volume_snapshot_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_snapshots.list()) AndReturn(self.volume_snapshots.list())
api.volume_create(IsA(http.HttpRequest), api.volume_create(IsA(http.HttpRequest),
@ -57,9 +60,10 @@ class VolumeViewTests(test.TestCase):
redirect_url = reverse('horizon:project:volumes:index') redirect_url = reverse('horizon:project:volumes:index')
self.assertRedirectsNoFollow(res, redirect_url) self.assertRedirectsNoFollow(res, redirect_url)
@test.create_stubs({api: ('tenant_quota_usages', 'volume_create', @test.create_stubs({api: ('volume_create',
'volume_snapshot_list'), 'volume_snapshot_list'),
api.nova: ('volume_snapshot_get',)}) cinder: ('volume_snapshot_get',),
quotas: ('tenant_quota_usages',)})
def test_create_volume_from_snapshot(self): def test_create_volume_from_snapshot(self):
volume = self.volumes.first() volume = self.volumes.first()
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
@ -70,9 +74,9 @@ class VolumeViewTests(test.TestCase):
'size': 50, 'snapshot_source': snapshot.id} 'size': 50, 'snapshot_source': snapshot.id}
# first call- with url param # first call- with url param
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
api.nova.volume_snapshot_get(IsA(http.HttpRequest), cinder.volume_snapshot_get(IsA(http.HttpRequest),
str(snapshot.id)).AndReturn(snapshot) str(snapshot.id)).AndReturn(snapshot)
api.volume_create(IsA(http.HttpRequest), api.volume_create(IsA(http.HttpRequest),
formData['size'], formData['size'],
formData['name'], formData['name'],
@ -80,11 +84,11 @@ class VolumeViewTests(test.TestCase):
snapshot_id=snapshot.id).\ snapshot_id=snapshot.id).\
AndReturn(volume) AndReturn(volume)
# second call- with dropdown # second call- with dropdown
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
api.volume_snapshot_list(IsA(http.HttpRequest)).\ api.volume_snapshot_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_snapshots.list()) AndReturn(self.volume_snapshots.list())
api.nova.volume_snapshot_get(IsA(http.HttpRequest), cinder.volume_snapshot_get(IsA(http.HttpRequest),
str(snapshot.id)).AndReturn(snapshot) str(snapshot.id)).AndReturn(snapshot)
api.volume_create(IsA(http.HttpRequest), api.volume_create(IsA(http.HttpRequest),
formData['size'], formData['size'],
formData['name'], formData['name'],
@ -110,8 +114,8 @@ class VolumeViewTests(test.TestCase):
redirect_url = reverse('horizon:project:volumes:index') redirect_url = reverse('horizon:project:volumes:index')
self.assertRedirectsNoFollow(res, redirect_url) self.assertRedirectsNoFollow(res, redirect_url)
@test.create_stubs({api: ('tenant_quota_usages',), @test.create_stubs({cinder: ('volume_snapshot_get',),
api.nova: ('volume_snapshot_get',)}) quotas: ('tenant_quota_usages',)})
def test_create_volume_from_snapshot_invalid_size(self): def test_create_volume_from_snapshot_invalid_size(self):
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
snapshot = self.volume_snapshots.first() snapshot = self.volume_snapshots.first()
@ -120,10 +124,10 @@ class VolumeViewTests(test.TestCase):
'method': u'CreateForm', 'method': u'CreateForm',
'size': 20, 'snapshot_source': snapshot.id} 'size': 20, 'snapshot_source': snapshot.id}
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
api.nova.volume_snapshot_get(IsA(http.HttpRequest), cinder.volume_snapshot_get(IsA(http.HttpRequest),
str(snapshot.id)).AndReturn(snapshot) str(snapshot.id)).AndReturn(snapshot)
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
self.mox.ReplayAll() self.mox.ReplayAll()
@ -136,7 +140,8 @@ class VolumeViewTests(test.TestCase):
"The volume size cannot be less than the " "The volume size cannot be less than the "
"snapshot size (40GB)") "snapshot size (40GB)")
@test.create_stubs({api: ('tenant_quota_usages', 'volume_snapshot_list')}) @test.create_stubs({api: ('volume_snapshot_list',),
quotas: ('tenant_quota_usages',)})
def test_create_volume_gb_used_over_alloted_quota(self): def test_create_volume_gb_used_over_alloted_quota(self):
usage = {'gigabytes': {'available': 100, 'used': 20}} usage = {'gigabytes': {'available': 100, 'used': 20}}
formData = {'name': u'This Volume Is Huge!', formData = {'name': u'This Volume Is Huge!',
@ -144,10 +149,10 @@ class VolumeViewTests(test.TestCase):
'method': u'CreateForm', 'method': u'CreateForm',
'size': 5000} 'size': 5000}
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
api.volume_snapshot_list(IsA(http.HttpRequest)).\ api.volume_snapshot_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_snapshots.list()) AndReturn(self.volume_snapshots.list())
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
self.mox.ReplayAll() self.mox.ReplayAll()
@ -158,7 +163,8 @@ class VolumeViewTests(test.TestCase):
' have 100GB of your quota available.'] ' have 100GB of your quota available.']
self.assertEqual(res.context['form'].errors['__all__'], expected_error) self.assertEqual(res.context['form'].errors['__all__'], expected_error)
@test.create_stubs({api: ('tenant_quota_usages', 'volume_snapshot_list')}) @test.create_stubs({api: ('volume_snapshot_list',),
quotas: ('tenant_quota_usages',)})
def test_create_volume_number_over_alloted_quota(self): def test_create_volume_number_over_alloted_quota(self):
usage = {'gigabytes': {'available': 100, 'used': 20}, usage = {'gigabytes': {'available': 100, 'used': 20},
'volumes': {'available': 0}} 'volumes': {'available': 0}}
@ -167,10 +173,10 @@ class VolumeViewTests(test.TestCase):
'method': u'CreateForm', 'method': u'CreateForm',
'size': 10} 'size': 10}
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
api.volume_snapshot_list(IsA(http.HttpRequest)).\ api.volume_snapshot_list(IsA(http.HttpRequest)).\
AndReturn(self.volume_snapshots.list()) AndReturn(self.volume_snapshots.list())
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
self.mox.ReplayAll() self.mox.ReplayAll()
@ -297,14 +303,15 @@ class VolumeViewTests(test.TestCase):
server.id) server.id)
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)
@test.create_stubs({api.nova: ('volume_get', 'server_get',)}) @test.create_stubs({cinder: ('volume_get',),
api.nova: ('server_get',)})
def test_detail_view(self): def test_detail_view(self):
volume = self.volumes.first() volume = self.volumes.first()
server = self.servers.first() server = self.servers.first()
volume.attachments = [{"server_id": server.id}] volume.attachments = [{"server_id": server.id}]
api.nova.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume) cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
self.mox.ReplayAll() self.mox.ReplayAll()

View File

@ -30,6 +30,7 @@ from horizon import tables
from horizon import tabs from horizon import tabs
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.usage import quotas
from .forms import CreateForm, AttachForm, CreateSnapshotForm from .forms import CreateForm, AttachForm, CreateSnapshotForm
from .tables import AttachmentsTable, VolumesTable from .tables import AttachmentsTable, VolumesTable
from .tabs import VolumeDetailTabs from .tabs import VolumeDetailTabs
@ -93,7 +94,7 @@ class CreateView(forms.ModalFormView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(CreateView, self).get_context_data(**kwargs) context = super(CreateView, self).get_context_data(**kwargs)
try: try:
context['usages'] = api.tenant_quota_usages(self.request) context['usages'] = quotas.tenant_quota_usages(self.request)
except: except:
exceptions.handle(self.request) exceptions.handle(self.request)
return context return context

View File

@ -96,7 +96,7 @@ class ComputeApiTests(test.APITestCase):
self.mox.ReplayAll() self.mox.ReplayAll()
ret_val = api.usage_get(self.request, self.tenant.id, 'start', 'end') ret_val = api.usage_get(self.request, self.tenant.id, 'start', 'end')
self.assertIsInstance(ret_val, api.nova.Usage) self.assertIsInstance(ret_val, api.nova.NovaUsage)
def test_usage_list(self): def test_usage_list(self):
usages = self.usages.list() usages = self.usages.list()
@ -108,7 +108,7 @@ class ComputeApiTests(test.APITestCase):
ret_val = api.usage_list(self.request, 'start', 'end') ret_val = api.usage_list(self.request, 'start', 'end')
for usage in ret_val: for usage in ret_val:
self.assertIsInstance(usage, api.Usage) self.assertIsInstance(usage, api.NovaUsage)
def test_server_get(self): def test_server_get(self):
server = self.servers.first() server = self.servers.first()
@ -156,51 +156,3 @@ class ComputeApiTests(test.APITestCase):
server.id, server.id,
floating_ip.id) floating_ip.id)
self.assertIsInstance(server, api.nova.Server) self.assertIsInstance(server, api.nova.Server)
@test.create_stubs({api.nova: ('volume_list',
'server_list',
'flavor_list',
'tenant_floating_ip_list',
'tenant_quota_get',)})
def test_tenant_quota_usages(self):
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.quotas.first())
api.nova.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn(self.servers.list())
api.nova.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
self.mox.ReplayAll()
quota_usages = api.tenant_quota_usages(self.request)
expected_output = {'gigabytes': {
'used': 80,
'flavor_fields': [],
'quota': 1000},
'ram': {
'available': 8976,
'used': 1024,
'flavor_fields': ['ram'],
'quota': 10000},
'floating_ips': {
'used': 2,
'flavor_fields': [],
'quota': 1},
'instances': {
'used': 2,
'flavor_fields': [],
'quota': 10},
'volumes': {
'used': 3,
'flavor_fields': [],
'quota': 1},
'cores': {
'used': 2,
'flavor_fields': ['vcpus'],
'quota': 10}}
self.assertEquals(quota_usages, expected_output)

View File

@ -20,6 +20,8 @@ from novaclient.v1_1 import (flavors, keypairs, servers, volumes, quotas,
security_group_rules as rules, security_group_rules as rules,
security_groups as sec_groups) security_groups as sec_groups)
from openstack_dashboard.api.base import Quota, QuotaSet as QuotaSetWrapper
from openstack_dashboard.usage.quotas import QuotaUsage
from .utils import TestDataContainer from .utils import TestDataContainer
@ -265,21 +267,23 @@ def data(TEST):
injected_files='1', injected_files='1',
cores='10') cores='10')
quota = quotas.QuotaSet(quotas.QuotaSetManager(None), quota_data) quota = quotas.QuotaSet(quotas.QuotaSetManager(None), quota_data)
TEST.quotas.add(quota) TEST.quotas.add(QuotaSetWrapper(quota))
# Quota Usages # Quota Usages
TEST.quota_usages.add({'gigabytes': {'available': 1000, quota_usage_data = {'gigabytes': {'used': 0,
'used': 0, 'quota': 1000},
'quota': 1000}, 'instances': {'used': 0,
'instances': {'available': 10, 'quota': 10},
'used': 0, 'ram': {'used': 0,
'quota': 10}, 'quota': 10000},
'ram': {'available': 10000, 'cores': {'used': 0,
'used': 0, 'quota': 20}}
'quota': 10000}, quota_usage = QuotaUsage()
'cores': {'available': 20, for k, v in quota_usage_data.items():
'used': 0, quota_usage.add_quota(Quota(k, v['quota']))
'quota': 20}}) quota_usage.tally(k, v['used'])
TEST.quota_usages.add(quota_usage)
# Servers # Servers
vals = {"host": "http://nova.example.com:8774", vals = {"host": "http://nova.example.com:8774",

View File

@ -0,0 +1,69 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
# Copyright (c) 2012 X.commerce, a business unit of eBay 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.
from __future__ import absolute_import
from django import http
from mox import IsA
from openstack_dashboard import api
from openstack_dashboard.api import cinder
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
class QuotaTests(test.APITestCase):
@test.create_stubs({api.nova: ('server_list',
'flavor_list',
'tenant_floating_ip_list',
'tenant_quota_get',),
cinder: ('volume_list', 'tenant_quota_get',)})
def test_tenant_quota_usages(self):
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.quotas.first())
api.nova.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn(self.servers.list())
cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.quotas.first())
self.mox.ReplayAll()
quota_usages = quotas.tenant_quota_usages(self.request)
expected_output = {
'injected_file_content_bytes': {'quota': 1},
'metadata_items': {'quota': 1},
'injected_files': {'quota': 1},
'gigabytes': {'available': 920, 'used': 80, 'quota': 1000},
'ram': {'available': 8976, 'used': 1024, 'quota': 10000},
'floating_ips': {'available': 0, 'used': 2, 'quota': 1},
'instances': {'available': 8, 'used': 2, 'quota': 10},
'volumes': {'available': 0, 'used': 3, 'quota': 1},
'cores': {'available': 8, 'used': 2, 'quota': 10}
}
# Compare internal structure of usages to expected.
self.assertEquals(quota_usages.usages, expected_output)

View File

@ -12,6 +12,7 @@ from horizon import forms
from horizon import messages from horizon import messages
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.usage import quotas
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -108,7 +109,7 @@ class BaseUsage(object):
def get_quotas(self): def get_quotas(self):
try: try:
self.quotas = api.nova.tenant_quota_usages(self.request) self.quotas = quotas.tenant_quota_usages(self.request)
except: except:
exceptions.handle(self.request, exceptions.handle(self.request,
_("Unable to retrieve quota information.")) _("Unable to retrieve quota information."))

View File

@ -0,0 +1,108 @@
from collections import defaultdict
import itertools
from horizon import exceptions
from horizon.utils.memoized import memoized
from openstack_dashboard.api import nova, cinder
from openstack_dashboard.api.base import is_service_enabled, QuotaSet
class QuotaUsage(dict):
""" Tracks quota limit, used, and available for a given set of quotas."""
def __init__(self):
self.usages = defaultdict(dict)
def __getitem__(self, key):
return self.usages[key]
def __setitem__(self, key, value):
raise NotImplemented("Directly setting QuotaUsage values is not "
"supported. Please use the add_quota and "
"tally methods.")
def __repr__(self):
return repr(dict(self.usages))
def add_quota(self, quota):
""" Adds an internal tracking reference for the given quota. """
if quota.limit is None:
# Handle "unlimited" quotas.
self.usages[quota.name]['quota'] = float("inf")
self.usages[quota.name]['available'] = float("inf")
else:
self.usages[quota.name]['quota'] = int(quota.limit)
def tally(self, name, value):
""" Adds to the "used" metric for the given quota. """
value = value or 0 # Protection against None.
# Start at 0 if this is the first value.
if 'used' not in self.usages[name]:
self.usages[name]['used'] = 0
# Increment our usage and update the "available" metric.
self.usages[name]['used'] += int(value) # Fail if can't coerce to int.
self.update_available(name)
def update_available(self, name):
""" Updates the "available" metric for the given quota. """
available = self.usages[name]['quota'] - self.usages[name]['used']
if available < 0:
available = 0
self.usages[name]['available'] = available
def get_quota_data(request, method_name):
quotasets = []
tenant_id = request.user.tenant_id
quotasets.append(getattr(nova, method_name)(request, tenant_id))
if is_service_enabled(request, 'volume'):
quotasets.append(getattr(cinder, method_name)(request, tenant_id))
qs = QuotaSet()
for quota in itertools.chain(*quotasets):
qs[quota.name] = quota.limit
return qs
def get_default_quota_data(request):
return get_quota_data(request, "default_quota_get")
def get_tenant_quota_data(request):
return get_quota_data(request, "tenant_quota_get")
@memoized
def tenant_quota_usages(request):
# Get our quotas and construct our usage object.
usages = QuotaUsage()
for quota in get_tenant_quota_data(request):
usages.add_quota(quota)
# Get our usages.
floating_ips = nova.tenant_floating_ip_list(request)
flavors = dict([(f.id, f) for f in nova.flavor_list(request)])
volumes = cinder.volume_list(request)
instances = nova.server_list(request)
# Fetch deleted flavors if necessary.
missing_flavors = [instance.flavor['id'] for instance in instances
if instance.flavor['id'] not in flavors]
for missing in missing_flavors:
if missing not in flavors:
try:
flavors[missing] = nova.flavor_get(request, missing)
except:
flavors[missing] = {}
exceptions.handle(request, ignore=True)
usages.tally('instances', len(instances))
usages.tally('floating_ips', len(floating_ips))
usages.tally('volumes', len(volumes))
usages.tally('gigabytes', sum([int(v.size) for v in volumes]))
# Sum our usage based on the flavors of the instances.
for flavor in [flavors[instance.flavor['id']] for instance in instances]:
usages.tally('cores', getattr(flavor, 'vcpus', None))
usages.tally('ram', getattr(flavor, 'ram', None))
return usages