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:
parent
a0ad0d49a9
commit
cdcd8e3df6
@ -140,7 +140,7 @@ horizon.Quota = {
|
||||
element.removeClass('progress_bar_over');
|
||||
}
|
||||
|
||||
element.animate({width: update_width + "%"}, 300);
|
||||
element.animate({width: parseInt(update_width, 10) + "%"}, 300);
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -45,3 +45,6 @@ class memoized(object):
|
||||
def __get__(self, obj, objtype):
|
||||
'''Support instance methods.'''
|
||||
return functools.partial(self.__call__, obj)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.func)
|
||||
|
@ -37,3 +37,4 @@ from openstack_dashboard.api.keystone import *
|
||||
from openstack_dashboard.api.nova import *
|
||||
from openstack_dashboard.api.swift import *
|
||||
from openstack_dashboard.api.quantum import *
|
||||
from openstack_dashboard.api.cinder import *
|
||||
|
@ -18,6 +18,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from collections import Sequence
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
@ -92,6 +93,53 @@ class APIDictWrapper(object):
|
||||
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):
|
||||
if 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)
|
||||
else:
|
||||
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
|
||||
|
115
openstack_dashboard/api/cinder.py
Normal file
115
openstack_dashboard/api/cinder.py
Normal 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))
|
@ -27,17 +27,14 @@ import logging
|
||||
from django.conf import settings
|
||||
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 security_group_rules as nova_rules
|
||||
from novaclient.v1_1.security_groups import SecurityGroup as NovaSecurityGroup
|
||||
from novaclient.v1_1.servers import REBOOT_HARD
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon.utils.memoized import memoized
|
||||
|
||||
from openstack_dashboard.api.base import (APIResourceWrapper,
|
||||
from openstack_dashboard.api.base import (APIResourceWrapper, QuotaSet,
|
||||
APIDictWrapper, url_for)
|
||||
|
||||
|
||||
@ -56,32 +53,6 @@ class VNCConsole(APIDictWrapper):
|
||||
_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):
|
||||
"""Simple wrapper around novaclient.server.Server
|
||||
|
||||
@ -117,7 +88,7 @@ class Server(APIResourceWrapper):
|
||||
novaclient(self.request).servers.reboot(self.id, hardness)
|
||||
|
||||
|
||||
class Usage(APIResourceWrapper):
|
||||
class NovaUsage(APIResourceWrapper):
|
||||
"""Simple wrapper around contrib/simple_usage.py."""
|
||||
_attrs = ['start', 'server_usages', 'stop', 'tenant_id',
|
||||
'total_local_gb_usage', 'total_memory_mb_usage',
|
||||
@ -210,20 +181,6 @@ def novaclient(request):
|
||||
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'):
|
||||
return VNCConsole(novaclient(request).servers.get_vnc_console(instance_id,
|
||||
console_type)['console'])
|
||||
@ -408,68 +365,17 @@ def tenant_quota_update(request, 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))
|
||||
|
||||
|
||||
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):
|
||||
return [Usage(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
|
||||
return [NovaUsage(u) for u in
|
||||
novaclient(request).usage.list(start, end, True)]
|
||||
|
||||
|
||||
def security_group_list(request):
|
||||
@ -510,30 +416,28 @@ def virtual_interfaces_list(request, instance_id):
|
||||
return novaclient(request).virtual_interfaces.list(instance_id)
|
||||
|
||||
|
||||
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 get_x509_credentials(request):
|
||||
return novaclient(request).certs.create()
|
||||
|
||||
|
||||
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 = 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 get_x509_root_certificate(request):
|
||||
return novaclient(request).certs.get()
|
||||
|
||||
|
||||
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)
|
||||
|
||||
for volume in volumes:
|
||||
@ -541,47 +445,3 @@ def volume_instance_list(request, instance_id):
|
||||
volume.name = volume_data.display_name
|
||||
|
||||
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()
|
||||
|
@ -29,8 +29,9 @@ from mox import IsA, Func
|
||||
from horizon.templatetags.sizeformat import mbformat
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
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')
|
||||
@ -38,19 +39,19 @@ INDEX_URL = reverse('horizon:project:overview:index')
|
||||
|
||||
class UsageViewTests(test.BaseAdminViewTests):
|
||||
@test.create_stubs({api: ('usage_list',),
|
||||
api.nova: ('tenant_quota_usages',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.keystone: ('tenant_list',)})
|
||||
def test_usage(self):
|
||||
now = timezone.now()
|
||||
usage_obj = api.nova.Usage(self.usages.first())
|
||||
quotas = self.quota_usages.first()
|
||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||
quota_data = self.quota_usages.first()
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True) \
|
||||
.AndReturn(self.tenants.list())
|
||||
api.usage_list(IsA(http.HttpRequest),
|
||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
||||
Func(usage.almost_now)) \
|
||||
.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()
|
||||
res = self.client.get(reverse('horizon:admin:overview:index'))
|
||||
self.assertTemplateUsed(res, 'admin/overview/usage.html')
|
||||
@ -70,19 +71,19 @@ class UsageViewTests(test.BaseAdminViewTests):
|
||||
usage_obj.total_local_gb_usage))
|
||||
|
||||
@test.create_stubs({api: ('usage_list',),
|
||||
api.nova: ('tenant_quota_usages',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.keystone: ('tenant_list',)})
|
||||
def test_usage_csv(self):
|
||||
now = timezone.now()
|
||||
usage_obj = api.nova.Usage(self.usages.first())
|
||||
quotas = self.quota_usages.first()
|
||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||
quota_data = self.quota_usages.first()
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True) \
|
||||
.AndReturn(self.tenants.list())
|
||||
api.usage_list(IsA(http.HttpRequest),
|
||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
||||
Func(usage.almost_now)) \
|
||||
.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()
|
||||
csv_url = reverse('horizon:admin:overview:index') + "?format=csv"
|
||||
res = self.client.get(csv_url)
|
||||
|
@ -21,6 +21,7 @@ from mox import IsA
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
from openstack_dashboard.usage import quotas
|
||||
from .workflows import CreateProject, UpdateProject
|
||||
from .views import QUOTA_FIELDS
|
||||
|
||||
@ -55,7 +56,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
def _get_quota_info(self, quota):
|
||||
quota_data = {}
|
||||
for field in QUOTA_FIELDS:
|
||||
quota_data[field] = int(getattr(quota, field, None))
|
||||
quota_data[field] = int(quota.get(field).limit)
|
||||
return quota_data
|
||||
|
||||
def _get_workflow_data(self, project, quota):
|
||||
@ -64,8 +65,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
project_info.update(quota_data)
|
||||
return project_info
|
||||
|
||||
@test.create_stubs({api: ('tenant_quota_defaults',
|
||||
'get_default_role',),
|
||||
@test.create_stubs({api: ('get_default_role',),
|
||||
quotas: ('get_default_quota_data',),
|
||||
api.keystone: ('user_list',
|
||||
'role_list',)})
|
||||
def test_add_project_get(self):
|
||||
@ -74,8 +75,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
users = self.users.list()
|
||||
roles = self.roles.list()
|
||||
|
||||
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
# init
|
||||
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)
|
||||
|
||||
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'],
|
||||
quota.injected_files)
|
||||
quota.get('injected_files').limit)
|
||||
self.assertQuerysetEqual(workflow.steps,
|
||||
['<CreateProjectInfo: createprojectinfoaction>',
|
||||
'<UpdateProjectMembers: update_members>',
|
||||
'<UpdateProjectQuota: update_quotas>'])
|
||||
|
||||
@test.create_stubs({api: ('get_default_role',
|
||||
'tenant_quota_defaults',
|
||||
'add_tenant_user_role',),
|
||||
api.keystone: ('tenant_create',
|
||||
'user_list',
|
||||
'role_list'),
|
||||
quotas: ('get_default_quota_data',),
|
||||
api.cinder: ('tenant_quota_update',),
|
||||
api.nova: ('tenant_quota_update',)})
|
||||
def test_add_project_post(self):
|
||||
project = self.tenants.first()
|
||||
@ -116,8 +117,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
roles = self.roles.list()
|
||||
|
||||
# init
|
||||
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
|
||||
@ -159,8 +159,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api: ('tenant_quota_defaults',
|
||||
'get_default_role',),
|
||||
@test.create_stubs({api: ('get_default_role',),
|
||||
quotas: ('get_default_quota_data',),
|
||||
api.keystone: ('user_list',
|
||||
'role_list',)})
|
||||
def test_add_project_quota_defaults_error(self):
|
||||
@ -169,7 +169,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
roles = self.roles.list()
|
||||
|
||||
# init
|
||||
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)) \
|
||||
.AndRaise(self.exceptions.nova)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
@ -184,8 +184,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
self.assertTemplateUsed(res, 'admin/projects/create.html')
|
||||
self.assertContains(res, "Unable to retrieve default quota values")
|
||||
|
||||
@test.create_stubs({api: ('get_default_role',
|
||||
'tenant_quota_defaults',),
|
||||
@test.create_stubs({api: ('get_default_role',),
|
||||
quotas: ('get_default_quota_data',),
|
||||
api.keystone: ('tenant_create',
|
||||
'user_list',
|
||||
'role_list',)})
|
||||
@ -197,8 +197,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
roles = self.roles.list()
|
||||
|
||||
# init
|
||||
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
|
||||
@ -224,11 +223,11 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api: ('get_default_role',
|
||||
'tenant_quota_defaults',
|
||||
'add_tenant_user_role',),
|
||||
api.keystone: ('tenant_create',
|
||||
'user_list',
|
||||
'role_list'),
|
||||
quotas: ('get_default_quota_data',),
|
||||
api.nova: ('tenant_quota_update',)})
|
||||
def test_add_project_quota_update_error(self):
|
||||
project = self.tenants.first()
|
||||
@ -238,8 +237,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
roles = self.roles.list()
|
||||
|
||||
# init
|
||||
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
|
||||
@ -283,11 +281,11 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api: ('get_default_role',
|
||||
'tenant_quota_defaults',
|
||||
'add_tenant_user_role',),
|
||||
api.keystone: ('tenant_create',
|
||||
'user_list',
|
||||
'role_list',),
|
||||
quotas: ('get_default_quota_data',),
|
||||
api.nova: ('tenant_quota_update',)})
|
||||
def test_add_project_user_update_error(self):
|
||||
project = self.tenants.first()
|
||||
@ -297,8 +295,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
roles = self.roles.list()
|
||||
|
||||
# init
|
||||
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
|
||||
@ -343,8 +340,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api: ('get_default_role',
|
||||
'tenant_quota_defaults',),
|
||||
@test.create_stubs({api: ('get_default_role',),
|
||||
quotas: ('get_default_quota_data',),
|
||||
api.keystone: ('user_list',
|
||||
'role_list',)})
|
||||
def test_add_project_missing_field_error(self):
|
||||
@ -355,8 +352,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
roles = self.roles.list()
|
||||
|
||||
# init
|
||||
api.tenant_quota_defaults(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
|
||||
@ -380,13 +376,13 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
def _get_quota_info(self, quota):
|
||||
quota_data = {}
|
||||
for field in QUOTA_FIELDS:
|
||||
quota_data[field] = int(getattr(quota, field, None))
|
||||
quota_data[field] = int(quota.get(field).limit)
|
||||
return quota_data
|
||||
|
||||
@test.create_stubs({api: ('get_default_role',
|
||||
'roles_for_user',
|
||||
'tenant_get',
|
||||
'tenant_quota_get',),
|
||||
'tenant_get',),
|
||||
quotas: ('get_tenant_quota_data',),
|
||||
api.keystone: ('user_list',
|
||||
'role_list',)})
|
||||
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) \
|
||||
.AndReturn(project)
|
||||
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
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)
|
||||
|
||||
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'],
|
||||
quota.injected_files)
|
||||
quota.get('injected_files').limit)
|
||||
self.assertEqual(step.action.initial['name'], project.name)
|
||||
self.assertEqual(step.action.initial['description'],
|
||||
project.description)
|
||||
@ -434,13 +429,14 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
'<UpdateProjectQuota: update_quotas>'])
|
||||
|
||||
@test.create_stubs({api: ('tenant_get',
|
||||
'tenant_quota_get',
|
||||
'tenant_update',
|
||||
'tenant_quota_update',
|
||||
'get_default_role',
|
||||
'roles_for_user',
|
||||
'remove_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',
|
||||
'role_list',)})
|
||||
def test_update_project_save(self):
|
||||
@ -453,8 +449,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
# get/init
|
||||
api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \
|
||||
.AndReturn(project)
|
||||
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
|
||||
@ -524,9 +519,13 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
user_id='3',
|
||||
role_id='1')
|
||||
|
||||
api.tenant_quota_update(IsA(http.HttpRequest),
|
||||
api.nova.tenant_quota_update(IsA(http.HttpRequest),
|
||||
project.id,
|
||||
**updated_quota)
|
||||
api.cinder.tenant_quota_update(IsA(http.HttpRequest),
|
||||
project.id,
|
||||
volumes=updated_quota['volumes'],
|
||||
gigabytes=updated_quota['gigabytes'])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
# submit form data
|
||||
@ -559,13 +558,13 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api: ('tenant_get',
|
||||
'tenant_quota_get',
|
||||
'tenant_update',
|
||||
'tenant_quota_update',
|
||||
'get_default_role',
|
||||
'roles_for_user',
|
||||
'remove_tenant_user',
|
||||
'add_tenant_user_role'),
|
||||
quotas: ('get_tenant_quota_data',),
|
||||
api.nova: ('tenant_quota_update',),
|
||||
api.keystone: ('user_list',
|
||||
'role_list',)})
|
||||
def test_update_project_tenant_update_error(self):
|
||||
@ -578,8 +577,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
# get/init
|
||||
api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \
|
||||
.AndReturn(project)
|
||||
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
|
||||
@ -631,13 +629,13 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api: ('tenant_get',
|
||||
'tenant_quota_get',
|
||||
'tenant_update',
|
||||
'tenant_quota_update',
|
||||
'get_default_role',
|
||||
'roles_for_user',
|
||||
'remove_tenant_user_role',
|
||||
'add_tenant_user_role'),
|
||||
quotas: ('get_tenant_quota_data',),
|
||||
api.nova: ('tenant_quota_update',),
|
||||
api.keystone: ('user_list',
|
||||
'role_list',)})
|
||||
def test_update_project_quota_update_error(self):
|
||||
@ -650,8 +648,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
# get/init
|
||||
api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \
|
||||
.AndReturn(project)
|
||||
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
|
||||
@ -708,7 +705,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
user_id='3',
|
||||
role_id='2')
|
||||
|
||||
api.tenant_quota_update(IsA(http.HttpRequest),
|
||||
api.nova.tenant_quota_update(IsA(http.HttpRequest),
|
||||
project.id,
|
||||
**updated_quota).AndRaise(self.exceptions.nova)
|
||||
|
||||
@ -730,12 +727,12 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api: ('tenant_get',
|
||||
'tenant_quota_get',
|
||||
'tenant_update',
|
||||
'get_default_role',
|
||||
'roles_for_user',
|
||||
'remove_tenant_user_role',
|
||||
'add_tenant_user_role'),
|
||||
quotas: ('get_tenant_quota_data',),
|
||||
api.keystone: ('user_list',
|
||||
'role_list',)})
|
||||
def test_update_project_member_update_error(self):
|
||||
@ -748,8 +745,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
# get/init
|
||||
api.tenant_get(IsA(http.HttpRequest), self.tenant.id, admin=True) \
|
||||
.AndReturn(project)
|
||||
api.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(quota)
|
||||
quotas.get_tenant_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
|
||||
|
||||
api.get_default_role(IsA(http.HttpRequest)).AndReturn(default_role)
|
||||
api.keystone.user_list(IsA(http.HttpRequest)).AndReturn(users)
|
||||
|
@ -29,6 +29,7 @@ from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import usage
|
||||
from openstack_dashboard.usage import quotas
|
||||
from openstack_dashboard.dashboards.admin.users.views import CreateView
|
||||
from .forms import CreateUser
|
||||
from .tables import TenantsTable, TenantUsersTable, AddUsersTable
|
||||
@ -145,10 +146,9 @@ class CreateProjectView(workflows.WorkflowView):
|
||||
|
||||
# get initial quota defaults
|
||||
try:
|
||||
quota_defaults = api.tenant_quota_defaults(self.request,
|
||||
self.request.user.tenant_id)
|
||||
quota_defaults = quotas.get_default_quota_data(self.request)
|
||||
for field in QUOTA_FIELDS:
|
||||
initial[field] = getattr(quota_defaults, field, None)
|
||||
initial[field] = quota_defaults.get(field).limit
|
||||
|
||||
except:
|
||||
error_msg = _('Unable to retrieve default quota values.')
|
||||
@ -174,9 +174,9 @@ class UpdateProjectView(workflows.WorkflowView):
|
||||
initial[field] = getattr(project_info, field, None)
|
||||
|
||||
# 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:
|
||||
initial[field] = getattr(quota_data, field, None)
|
||||
initial[field] = quota_data.get(field).limit
|
||||
except:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve project details.'),
|
||||
|
@ -28,6 +28,8 @@ from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
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"
|
||||
@ -361,7 +363,10 @@ class UpdateProject(workflows.Workflow):
|
||||
# update the project quota
|
||||
ifcb = data['injected_file_content_bytes']
|
||||
try:
|
||||
api.tenant_quota_update(request,
|
||||
# TODO(gabriel): Once nova-volume is fully deprecated the
|
||||
# "volumes" and "gigabytes" quotas should no longer be sent to
|
||||
# the nova API to be updated anymore.
|
||||
nova.tenant_quota_update(request,
|
||||
project_id,
|
||||
metadata_items=data['metadata_items'],
|
||||
injected_file_content_bytes=ifcb,
|
||||
@ -372,6 +377,11 @@ class UpdateProject(workflows.Workflow):
|
||||
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
|
||||
except:
|
||||
exceptions.handle(request, _('Modified project information and '
|
||||
|
@ -38,7 +38,7 @@ class IndexView(tables.DataTableView):
|
||||
|
||||
def get_data(self):
|
||||
try:
|
||||
quota_set = api.tenant_quota_defaults(self.request,
|
||||
quota_set = api.default_quota_get(self.request,
|
||||
self.request.user.tenant_id)
|
||||
data = quota_set.items
|
||||
except:
|
||||
|
@ -31,6 +31,7 @@ from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.usage import quotas
|
||||
from .forms import FloatingIpAllocate
|
||||
from .workflows import IPAssociationWorkflow
|
||||
|
||||
@ -51,7 +52,7 @@ class AllocateView(forms.ModalFormView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AllocateView, self).get_context_data(**kwargs)
|
||||
try:
|
||||
context['usages'] = api.tenant_quota_usages(self.request)
|
||||
context['usages'] = quotas.tenant_quota_usages(self.request)
|
||||
except:
|
||||
exceptions.handle(self.request)
|
||||
return context
|
||||
|
@ -24,6 +24,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api import cinder
|
||||
from ...volumes import tables as volume_tables
|
||||
|
||||
|
||||
@ -57,7 +58,7 @@ class UpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api import cinder
|
||||
|
||||
|
||||
class OverviewTab(tabs.Tab):
|
||||
@ -32,8 +32,8 @@ class OverviewTab(tabs.Tab):
|
||||
def get_context_data(self, request):
|
||||
snapshot_id = self.tab_group.kwargs['snapshot_id']
|
||||
try:
|
||||
snapshot = api.nova.volume_snapshot_get(request, snapshot_id)
|
||||
volume = api.nova.volume_get(request, snapshot.volume_id)
|
||||
snapshot = cinder.volume_snapshot_get(request, snapshot_id)
|
||||
volume = cinder.volume_get(request, snapshot.volume_id)
|
||||
volume.display_name = None
|
||||
except:
|
||||
redirect = reverse('horizon:project:images_and_snapshots:index')
|
||||
|
@ -26,7 +26,9 @@ from django.utils.datastructures import SortedDict
|
||||
from mox import IsA, IgnoreArg
|
||||
|
||||
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
|
||||
from .tabs import InstanceDetailTabs
|
||||
from .workflows import LaunchInstance
|
||||
|
||||
@ -324,7 +326,7 @@ class InstanceTests(test.TestCase):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api: ("server_get",
|
||||
"volume_instance_list",
|
||||
"instance_volumes_list",
|
||||
"flavor_get",
|
||||
"server_security_groups")})
|
||||
def test_instance_details_volumes(self):
|
||||
@ -332,7 +334,7 @@ class InstanceTests(test.TestCase):
|
||||
volumes = [self.volumes.list()[1]]
|
||||
|
||||
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)
|
||||
api.flavor_get(IsA(http.HttpRequest),
|
||||
server.flavor['id']).AndReturn(self.flavors.first())
|
||||
@ -348,7 +350,7 @@ class InstanceTests(test.TestCase):
|
||||
self.assertItemsEqual(res.context['instance'].volumes, volumes)
|
||||
|
||||
@test.create_stubs({api: ("server_get",
|
||||
"volume_instance_list",
|
||||
"instance_volumes_list",
|
||||
"flavor_get",
|
||||
"server_security_groups")})
|
||||
def test_instance_details_volume_sorting(self):
|
||||
@ -356,7 +358,7 @@ class InstanceTests(test.TestCase):
|
||||
volumes = self.volumes.list()[1:3]
|
||||
|
||||
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)
|
||||
api.flavor_get(IsA(http.HttpRequest),
|
||||
server.flavor['id']).AndReturn(self.flavors.first())
|
||||
@ -376,14 +378,14 @@ class InstanceTests(test.TestCase):
|
||||
"/dev/hdk")
|
||||
|
||||
@test.create_stubs({api: ("server_get",
|
||||
"volume_instance_list",
|
||||
"instance_volumes_list",
|
||||
"flavor_get",
|
||||
"server_security_groups",)})
|
||||
def test_instance_details_metadata(self):
|
||||
server = self.servers.first()
|
||||
|
||||
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([])
|
||||
api.flavor_get(IsA(http.HttpRequest),
|
||||
server.flavor['id']).AndReturn(self.flavors.first())
|
||||
@ -582,21 +584,21 @@ class InstanceTests(test.TestCase):
|
||||
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.nova: ('tenant_quota_usages',
|
||||
'flavor_list',
|
||||
@test.create_stubs({api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'security_group_list',
|
||||
'volume_snapshot_list',
|
||||
'security_group_list',),
|
||||
cinder: ('volume_snapshot_list',
|
||||
'volume_list',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.quantum: ('network_list',),
|
||||
api.glance: ('image_list_detailed',)})
|
||||
def test_launch_instance_get(self):
|
||||
quota_usages = self.quota_usages.first()
|
||||
image = self.images.first()
|
||||
|
||||
api.nova.volume_list(IsA(http.HttpRequest)) \
|
||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
api.nova.volume_snapshot_list(IsA(http.HttpRequest)) \
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||
filters={'is_public': True,
|
||||
@ -613,7 +615,7 @@ class InstanceTests(test.TestCase):
|
||||
api.quantum.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
api.nova.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
.AndReturn(quota_usages)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
@ -646,13 +648,13 @@ class InstanceTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.quantum: ('network_list',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'security_group_list',
|
||||
'volume_list',
|
||||
'volume_snapshot_list',
|
||||
'tenant_quota_usages',
|
||||
'server_create',)})
|
||||
'server_create',),
|
||||
cinder: ('volume_list',
|
||||
'volume_snapshot_list',)})
|
||||
def test_launch_instance_post(self):
|
||||
flavor = self.flavors.first()
|
||||
image = self.images.first()
|
||||
@ -687,9 +689,9 @@ class InstanceTests(test.TestCase):
|
||||
api.quantum.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
api.nova.volume_list(IsA(http.HttpRequest)) \
|
||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||
.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),
|
||||
server.name,
|
||||
image.id,
|
||||
@ -725,11 +727,11 @@ class InstanceTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.quantum: ('network_list',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'security_group_list',
|
||||
'volume_list',
|
||||
'tenant_quota_usages',
|
||||
'security_group_list',),
|
||||
cinder: ('volume_list',
|
||||
'volume_snapshot_list',)})
|
||||
def test_launch_instance_post_no_images_available(self):
|
||||
flavor = self.flavors.first()
|
||||
@ -743,7 +745,7 @@ class InstanceTests(test.TestCase):
|
||||
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.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),
|
||||
filters={'is_public': True,
|
||||
'status': 'active'}) \
|
||||
@ -765,9 +767,9 @@ class InstanceTests(test.TestCase):
|
||||
.AndReturn(self.keypairs.list())
|
||||
api.nova.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
api.nova.volume_list(IsA(http.HttpRequest)) \
|
||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
api.nova.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -795,16 +797,16 @@ class InstanceTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.quantum: ('network_list',),
|
||||
api.nova: ('tenant_quota_usages',
|
||||
'flavor_list',
|
||||
quotas: ('tenant_quota_usages',),
|
||||
cinder: ('volume_list',
|
||||
'volume_snapshot_list',),
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'volume_list',
|
||||
'security_group_list',
|
||||
'volume_snapshot_list',)})
|
||||
'security_group_list',)})
|
||||
def test_launch_flavorlist_error(self):
|
||||
api.nova.volume_list(IsA(http.HttpRequest)) \
|
||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
api.nova.volume_snapshot_list(IsA(http.HttpRequest)) \
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||
filters={'is_public': True,
|
||||
@ -821,7 +823,7 @@ class InstanceTests(test.TestCase):
|
||||
api.quantum.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.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())
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndRaise(self.exceptions.nova)
|
||||
@ -845,8 +847,8 @@ class InstanceTests(test.TestCase):
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'security_group_list',
|
||||
'volume_list',
|
||||
'server_create',
|
||||
'server_create',),
|
||||
cinder: ('volume_list',
|
||||
'volume_snapshot_list',)})
|
||||
def test_launch_form_keystone_exception(self):
|
||||
flavor = self.flavors.first()
|
||||
@ -857,7 +859,7 @@ class InstanceTests(test.TestCase):
|
||||
customization_script = 'userData'
|
||||
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())
|
||||
api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
|
||||
api.nova.keypair_list(IgnoreArg()).AndReturn(self.keypairs.list())
|
||||
@ -878,7 +880,7 @@ class InstanceTests(test.TestCase):
|
||||
api.quantum.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.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),
|
||||
server.name,
|
||||
image.id,
|
||||
@ -912,11 +914,11 @@ class InstanceTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.quantum: ('network_list',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'security_group_list',
|
||||
'volume_list',
|
||||
'tenant_quota_usages',
|
||||
'security_group_list',),
|
||||
cinder: ('volume_list',
|
||||
'volume_snapshot_list',)})
|
||||
def test_launch_form_instance_count_error(self):
|
||||
flavor = self.flavors.first()
|
||||
@ -950,13 +952,13 @@ class InstanceTests(test.TestCase):
|
||||
api.quantum.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
api.nova.volume_list(IsA(http.HttpRequest)) \
|
||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||
.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)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
api.nova.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.quota_usages.first())
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
@ -165,7 +165,7 @@ class DetailView(tabs.TabView):
|
||||
try:
|
||||
instance_id = self.kwargs['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)
|
||||
# Sort by device name
|
||||
instance.volumes.sort(key=lambda vol: vol.device)
|
||||
|
@ -29,6 +29,8 @@ from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api import cinder
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -116,7 +118,7 @@ class VolumeOptionsAction(workflows.Action):
|
||||
def populate_volume_id_choices(self, request, context):
|
||||
volume_options = [("", _("Select Volume"))]
|
||||
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]
|
||||
volume_options.extend([self._get_volume_display_name(vol)
|
||||
for vol in volumes])
|
||||
@ -128,7 +130,7 @@ class VolumeOptionsAction(workflows.Action):
|
||||
def populate_volume_snapshot_id_choices(self, request, context):
|
||||
volume_options = [("", _("Select Volume Snapshot"))]
|
||||
try:
|
||||
snapshots = api.nova.volume_snapshot_list(self.request)
|
||||
snapshots = cinder.volume_snapshot_list(self.request)
|
||||
snapshots = [s for s in snapshots
|
||||
if s.status == api.VOLUME_STATE_AVAILABLE]
|
||||
volume_options.extend([self._get_volume_display_name(snap)
|
||||
@ -294,7 +296,7 @@ class SetInstanceDetailsAction(workflows.Action):
|
||||
def get_help_text(self):
|
||||
extra = {}
|
||||
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'])
|
||||
flavors = json.dumps([f._info for f in
|
||||
api.nova.flavor_list(self.request)])
|
||||
|
@ -27,8 +27,9 @@ from django.utils import timezone
|
||||
from mox import IsA, Func
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
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')
|
||||
@ -37,15 +38,15 @@ INDEX_URL = reverse('horizon:project:overview:index')
|
||||
class UsageViewTests(test.TestCase):
|
||||
def test_usage(self):
|
||||
now = timezone.now()
|
||||
usage_obj = api.nova.Usage(self.usages.first())
|
||||
quotas = self.quota_usages.first()
|
||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||
quota_data = self.quota_usages.first()
|
||||
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,
|
||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
||||
Func(usage.almost_now)) \
|
||||
.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()
|
||||
|
||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||
@ -69,17 +70,17 @@ class UsageViewTests(test.TestCase):
|
||||
|
||||
def test_usage_csv(self):
|
||||
now = timezone.now()
|
||||
usage_obj = api.nova.Usage(self.usages.first())
|
||||
quotas = self.quota_usages.first()
|
||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||
quota_data = self.quota_usages.first()
|
||||
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)
|
||||
api.usage_get(IsA(http.HttpRequest),
|
||||
self.tenant.id,
|
||||
timestamp,
|
||||
Func(usage.almost_now)) \
|
||||
.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()
|
||||
res = self.client.get(reverse('horizon:project:overview:index') +
|
||||
@ -89,16 +90,16 @@ class UsageViewTests(test.TestCase):
|
||||
|
||||
def test_usage_exception_usage(self):
|
||||
now = timezone.now()
|
||||
quotas = self.quota_usages.first()
|
||||
quota_data = self.quota_usages.first()
|
||||
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)
|
||||
api.usage_get(IsA(http.HttpRequest),
|
||||
self.tenant.id,
|
||||
timestamp,
|
||||
Func(usage.almost_now)) \
|
||||
.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()
|
||||
|
||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||
@ -107,16 +108,16 @@ class UsageViewTests(test.TestCase):
|
||||
|
||||
def test_usage_exception_quota(self):
|
||||
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.nova, 'tenant_quota_usages')
|
||||
self.mox.StubOutWithMock(quotas, 'tenant_quota_usages')
|
||||
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
|
||||
api.usage_get(IsA(http.HttpRequest),
|
||||
self.tenant.id,
|
||||
timestamp,
|
||||
Func(usage.almost_now)) \
|
||||
.AndReturn(usage_obj)
|
||||
api.nova.tenant_quota_usages(IsA(http.HttpRequest))\
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest))\
|
||||
.AndRaise(self.exceptions.nova)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -126,17 +127,17 @@ class UsageViewTests(test.TestCase):
|
||||
|
||||
def test_usage_default_tenant(self):
|
||||
now = timezone.now()
|
||||
usage_obj = api.nova.Usage(self.usages.first())
|
||||
quotas = self.quota_usages.first()
|
||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||
quota_data = self.quota_usages.first()
|
||||
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)
|
||||
api.usage_get(IsA(http.HttpRequest),
|
||||
self.tenant.id,
|
||||
timestamp,
|
||||
Func(usage.almost_now)) \
|
||||
.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()
|
||||
|
||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||
|
@ -19,6 +19,8 @@ from horizon.utils.fields import SelectWidget
|
||||
from horizon.utils.memoized import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api import cinder
|
||||
from openstack_dashboard.usage import quotas
|
||||
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
|
||||
# a volume, so we need to check for that scenario here before we
|
||||
# send it off to try and create.
|
||||
usages = api.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
|
||||
snapshot_id = None
|
||||
if (data.get("snapshot_source", None)):
|
||||
@ -116,7 +118,7 @@ class CreateForm(forms.SelfHandlingForm):
|
||||
|
||||
@memoized
|
||||
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):
|
||||
@ -170,7 +172,7 @@ class AttachForm(forms.SelfHandlingForm):
|
||||
# it, so let's slice that off...
|
||||
instance_name = instance_name.rsplit(" (")[0]
|
||||
try:
|
||||
vol = api.volume_attach(request,
|
||||
vol = api.instance_volume_attach(request,
|
||||
data['volume_id'],
|
||||
data['instance'],
|
||||
data.get('device', ''))
|
||||
|
@ -185,7 +185,9 @@ class DetachVolume(tables.BatchAction):
|
||||
|
||||
def action(self, request, 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):
|
||||
return reverse('horizon:project:volumes:index')
|
||||
|
@ -20,7 +20,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api import cinder, nova
|
||||
|
||||
|
||||
class OverviewTab(tabs.Tab):
|
||||
@ -32,10 +32,9 @@ class OverviewTab(tabs.Tab):
|
||||
def get_context_data(self, request):
|
||||
volume_id = self.tab_group.kwargs['volume_id']
|
||||
try:
|
||||
volume = api.nova.volume_get(request, volume_id)
|
||||
volume = cinder.volume_get(request, volume_id)
|
||||
for att in volume.attachments:
|
||||
att['instance'] = api.nova.server_get(request,
|
||||
att['server_id'])
|
||||
att['instance'] = nova.server_get(request, att['server_id'])
|
||||
except:
|
||||
redirect = reverse('horizon:project:volumes:index')
|
||||
exceptions.handle(self.request,
|
||||
|
@ -26,12 +26,15 @@ from django.forms import widgets
|
||||
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 VolumeViewTests(test.TestCase):
|
||||
@test.create_stubs({api: ('tenant_quota_usages', 'volume_create',
|
||||
'volume_snapshot_list')})
|
||||
@test.create_stubs({api: ('volume_create',
|
||||
'volume_snapshot_list'),
|
||||
quotas: ('tenant_quota_usages',)})
|
||||
def test_create_volume(self):
|
||||
volume = self.volumes.first()
|
||||
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
|
||||
@ -40,7 +43,7 @@ class VolumeViewTests(test.TestCase):
|
||||
'method': u'CreateForm',
|
||||
'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)).\
|
||||
AndReturn(self.volume_snapshots.list())
|
||||
api.volume_create(IsA(http.HttpRequest),
|
||||
@ -57,9 +60,10 @@ class VolumeViewTests(test.TestCase):
|
||||
redirect_url = reverse('horizon:project:volumes:index')
|
||||
self.assertRedirectsNoFollow(res, redirect_url)
|
||||
|
||||
@test.create_stubs({api: ('tenant_quota_usages', 'volume_create',
|
||||
@test.create_stubs({api: ('volume_create',
|
||||
'volume_snapshot_list'),
|
||||
api.nova: ('volume_snapshot_get',)})
|
||||
cinder: ('volume_snapshot_get',),
|
||||
quotas: ('tenant_quota_usages',)})
|
||||
def test_create_volume_from_snapshot(self):
|
||||
volume = self.volumes.first()
|
||||
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
|
||||
@ -70,8 +74,8 @@ class VolumeViewTests(test.TestCase):
|
||||
'size': 50, 'snapshot_source': snapshot.id}
|
||||
|
||||
# first call- with url param
|
||||
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
|
||||
api.nova.volume_snapshot_get(IsA(http.HttpRequest),
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
|
||||
cinder.volume_snapshot_get(IsA(http.HttpRequest),
|
||||
str(snapshot.id)).AndReturn(snapshot)
|
||||
api.volume_create(IsA(http.HttpRequest),
|
||||
formData['size'],
|
||||
@ -80,10 +84,10 @@ class VolumeViewTests(test.TestCase):
|
||||
snapshot_id=snapshot.id).\
|
||||
AndReturn(volume)
|
||||
# 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)).\
|
||||
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)
|
||||
api.volume_create(IsA(http.HttpRequest),
|
||||
formData['size'],
|
||||
@ -110,8 +114,8 @@ class VolumeViewTests(test.TestCase):
|
||||
redirect_url = reverse('horizon:project:volumes:index')
|
||||
self.assertRedirectsNoFollow(res, redirect_url)
|
||||
|
||||
@test.create_stubs({api: ('tenant_quota_usages',),
|
||||
api.nova: ('volume_snapshot_get',)})
|
||||
@test.create_stubs({cinder: ('volume_snapshot_get',),
|
||||
quotas: ('tenant_quota_usages',)})
|
||||
def test_create_volume_from_snapshot_invalid_size(self):
|
||||
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
|
||||
snapshot = self.volume_snapshots.first()
|
||||
@ -120,10 +124,10 @@ class VolumeViewTests(test.TestCase):
|
||||
'method': u'CreateForm',
|
||||
'size': 20, 'snapshot_source': snapshot.id}
|
||||
|
||||
api.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
|
||||
api.nova.volume_snapshot_get(IsA(http.HttpRequest),
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
|
||||
cinder.volume_snapshot_get(IsA(http.HttpRequest),
|
||||
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()
|
||||
|
||||
@ -136,7 +140,8 @@ class VolumeViewTests(test.TestCase):
|
||||
"The volume size cannot be less than the "
|
||||
"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):
|
||||
usage = {'gigabytes': {'available': 100, 'used': 20}}
|
||||
formData = {'name': u'This Volume Is Huge!',
|
||||
@ -144,10 +149,10 @@ class VolumeViewTests(test.TestCase):
|
||||
'method': u'CreateForm',
|
||||
'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)).\
|
||||
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()
|
||||
|
||||
@ -158,7 +163,8 @@ class VolumeViewTests(test.TestCase):
|
||||
' have 100GB of your quota available.']
|
||||
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):
|
||||
usage = {'gigabytes': {'available': 100, 'used': 20},
|
||||
'volumes': {'available': 0}}
|
||||
@ -167,10 +173,10 @@ class VolumeViewTests(test.TestCase):
|
||||
'method': u'CreateForm',
|
||||
'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)).\
|
||||
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()
|
||||
|
||||
@ -297,14 +303,15 @@ class VolumeViewTests(test.TestCase):
|
||||
server.id)
|
||||
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):
|
||||
volume = self.volumes.first()
|
||||
server = self.servers.first()
|
||||
|
||||
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)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
@ -30,6 +30,7 @@ from horizon import tables
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.usage import quotas
|
||||
from .forms import CreateForm, AttachForm, CreateSnapshotForm
|
||||
from .tables import AttachmentsTable, VolumesTable
|
||||
from .tabs import VolumeDetailTabs
|
||||
@ -93,7 +94,7 @@ class CreateView(forms.ModalFormView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CreateView, self).get_context_data(**kwargs)
|
||||
try:
|
||||
context['usages'] = api.tenant_quota_usages(self.request)
|
||||
context['usages'] = quotas.tenant_quota_usages(self.request)
|
||||
except:
|
||||
exceptions.handle(self.request)
|
||||
return context
|
||||
|
@ -96,7 +96,7 @@ class ComputeApiTests(test.APITestCase):
|
||||
self.mox.ReplayAll()
|
||||
|
||||
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):
|
||||
usages = self.usages.list()
|
||||
@ -108,7 +108,7 @@ class ComputeApiTests(test.APITestCase):
|
||||
|
||||
ret_val = api.usage_list(self.request, 'start', 'end')
|
||||
for usage in ret_val:
|
||||
self.assertIsInstance(usage, api.Usage)
|
||||
self.assertIsInstance(usage, api.NovaUsage)
|
||||
|
||||
def test_server_get(self):
|
||||
server = self.servers.first()
|
||||
@ -156,51 +156,3 @@ class ComputeApiTests(test.APITestCase):
|
||||
server.id,
|
||||
floating_ip.id)
|
||||
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)
|
||||
|
@ -20,6 +20,8 @@ from novaclient.v1_1 import (flavors, keypairs, servers, volumes, quotas,
|
||||
security_group_rules as rules,
|
||||
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
|
||||
|
||||
|
||||
@ -265,21 +267,23 @@ def data(TEST):
|
||||
injected_files='1',
|
||||
cores='10')
|
||||
quota = quotas.QuotaSet(quotas.QuotaSetManager(None), quota_data)
|
||||
TEST.quotas.add(quota)
|
||||
TEST.quotas.add(QuotaSetWrapper(quota))
|
||||
|
||||
# Quota Usages
|
||||
TEST.quota_usages.add({'gigabytes': {'available': 1000,
|
||||
'used': 0,
|
||||
quota_usage_data = {'gigabytes': {'used': 0,
|
||||
'quota': 1000},
|
||||
'instances': {'available': 10,
|
||||
'used': 0,
|
||||
'instances': {'used': 0,
|
||||
'quota': 10},
|
||||
'ram': {'available': 10000,
|
||||
'used': 0,
|
||||
'ram': {'used': 0,
|
||||
'quota': 10000},
|
||||
'cores': {'available': 20,
|
||||
'used': 0,
|
||||
'quota': 20}})
|
||||
'cores': {'used': 0,
|
||||
'quota': 20}}
|
||||
quota_usage = QuotaUsage()
|
||||
for k, v in quota_usage_data.items():
|
||||
quota_usage.add_quota(Quota(k, v['quota']))
|
||||
quota_usage.tally(k, v['used'])
|
||||
|
||||
TEST.quota_usages.add(quota_usage)
|
||||
|
||||
# Servers
|
||||
vals = {"host": "http://nova.example.com:8774",
|
||||
|
69
openstack_dashboard/test/tests/quotas.py
Normal file
69
openstack_dashboard/test/tests/quotas.py
Normal 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)
|
@ -12,6 +12,7 @@ from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -108,7 +109,7 @@ class BaseUsage(object):
|
||||
|
||||
def get_quotas(self):
|
||||
try:
|
||||
self.quotas = api.nova.tenant_quota_usages(self.request)
|
||||
self.quotas = quotas.tenant_quota_usages(self.request)
|
||||
except:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve quota information."))
|
||||
|
108
openstack_dashboard/usage/quotas.py
Normal file
108
openstack_dashboard/usage/quotas.py
Normal 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
|
Loading…
Reference in New Issue
Block a user