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.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):
|
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)
|
||||||
|
@ -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 *
|
||||||
|
@ -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
|
||||||
|
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.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()
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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.'),
|
||||||
|
@ -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 '
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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')
|
||||||
|
@ -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()
|
||||||
|
@ -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,
|
||||||
|
@ -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)])
|
||||||
|
@ -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'))
|
||||||
|
@ -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 '
|
||||||
|
@ -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')
|
||||||
|
@ -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,
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
|
||||||
|
@ -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",
|
||||||
|
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 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."))
|
||||||
|
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