Merge "Replace "tenant_id" with "project_id" in Quota engine"
This commit is contained in:
commit
d3fa1ac3bf
@ -245,7 +245,7 @@ The process of making a reservation is fairly straightforward:
|
||||
on every requested resource, and then retrieving the amount of reserved
|
||||
resources.
|
||||
* Fetch current quota limits for requested resources, by invoking the
|
||||
_get_tenant_quotas method.
|
||||
_get_project_quotas method.
|
||||
* Fetch expired reservations for selected resources. This amount will be
|
||||
subtracted from resource usage. As in most cases there won't be any
|
||||
expired reservation, this approach actually requires less DB operations than
|
||||
|
@ -401,8 +401,10 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
||||
def create_network_db(self, context, network):
|
||||
# single request processing
|
||||
n = network['network']
|
||||
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||
project_id = n.get('project_id') or n['tenant_id']
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
args = {'tenant_id': n['tenant_id'],
|
||||
args = {'tenant_id': project_id,
|
||||
'id': n.get('id') or uuidutils.generate_uuid(),
|
||||
'name': n['name'],
|
||||
'mtu': n.get('mtu', constants.DEFAULT_NETWORK_MTU),
|
||||
@ -1426,12 +1428,14 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
||||
p = port['port']
|
||||
port_id = p.get('id') or uuidutils.generate_uuid()
|
||||
network_id = p['network_id']
|
||||
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||
project_id = p.get('project_id') or p['tenant_id']
|
||||
if p.get('device_owner'):
|
||||
self._enforce_device_owner_not_router_intf_or_device_id(
|
||||
context, p.get('device_owner'), p.get('device_id'),
|
||||
p['tenant_id'])
|
||||
project_id)
|
||||
|
||||
port_data = dict(tenant_id=p['tenant_id'],
|
||||
port_data = dict(tenant_id=project_id,
|
||||
name=p['name'],
|
||||
id=port_id,
|
||||
network_id=network_id,
|
||||
|
@ -31,28 +31,28 @@ def utcnow():
|
||||
|
||||
|
||||
class QuotaUsageInfo(collections.namedtuple(
|
||||
'QuotaUsageInfo', ['resource', 'tenant_id', 'used', 'dirty'])):
|
||||
'QuotaUsageInfo', ['resource', 'project_id', 'used', 'dirty'])):
|
||||
"""Information about resource quota usage."""
|
||||
|
||||
|
||||
class ReservationInfo(collections.namedtuple(
|
||||
'ReservationInfo', ['reservation_id', 'tenant_id',
|
||||
'ReservationInfo', ['reservation_id', 'project_id',
|
||||
'expiration', 'deltas'])):
|
||||
"""Information about a resource reservation."""
|
||||
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_quota_usage_by_resource_and_tenant(context, resource, tenant_id):
|
||||
"""Return usage info for a given resource and tenant.
|
||||
def get_quota_usage_by_resource_and_project(context, resource, project_id):
|
||||
"""Return usage info for a given resource and project.
|
||||
|
||||
:param context: Request context
|
||||
:param resource: Name of the resource
|
||||
:param tenant_id: Tenant identifier
|
||||
:param project_id: Project identifier
|
||||
:returns: a QuotaUsageInfo instance
|
||||
"""
|
||||
|
||||
result = quota_obj.QuotaUsage.get_object_dirty_protected(
|
||||
context, resource=resource, project_id=tenant_id)
|
||||
context, resource=resource, project_id=project_id)
|
||||
if not result:
|
||||
return
|
||||
return QuotaUsageInfo(result.resource, result.project_id, result.in_use,
|
||||
@ -69,23 +69,22 @@ def get_quota_usage_by_resource(context, resource):
|
||||
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_quota_usage_by_tenant_id(context, tenant_id):
|
||||
objs = quota_obj.QuotaUsage.get_objects(context, project_id=tenant_id)
|
||||
def get_quota_usage_by_project_id(context, project_id):
|
||||
objs = quota_obj.QuotaUsage.get_objects(context, project_id=project_id)
|
||||
return [QuotaUsageInfo(item.resource,
|
||||
tenant_id,
|
||||
project_id,
|
||||
item.in_use,
|
||||
item.dirty) for item in objs]
|
||||
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def set_quota_usage(context, resource, tenant_id,
|
||||
in_use=None, delta=False):
|
||||
def set_quota_usage(context, resource, project_id, in_use=None, delta=False):
|
||||
"""Set resource quota usage.
|
||||
|
||||
:param context: instance of neutron context with db session
|
||||
:param resource: name of the resource for which usage is being set
|
||||
:param tenant_id: identifier of the tenant for which quota usage is
|
||||
being set
|
||||
:param project_id: identifier of the project for which quota usage is
|
||||
being set
|
||||
:param in_use: integer specifying the new quantity of used resources,
|
||||
or a delta to apply to current used resource
|
||||
:param delta: Specifies whether in_use is an absolute number
|
||||
@ -93,11 +92,11 @@ def set_quota_usage(context, resource, tenant_id,
|
||||
"""
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
usage_data = quota_obj.QuotaUsage.get_object(
|
||||
context, resource=resource, project_id=tenant_id)
|
||||
context, resource=resource, project_id=project_id)
|
||||
if not usage_data:
|
||||
# Must create entry
|
||||
usage_data = quota_obj.QuotaUsage(
|
||||
context, resource=resource, project_id=tenant_id)
|
||||
context, resource=resource, project_id=project_id)
|
||||
usage_data.create()
|
||||
# Perform explicit comparison with None as 0 is a valid value
|
||||
if in_use is not None:
|
||||
@ -113,16 +112,16 @@ def set_quota_usage(context, resource, tenant_id,
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
@db_api.CONTEXT_WRITER
|
||||
def set_quota_usage_dirty(context, resource, tenant_id, dirty=True):
|
||||
"""Set quota usage dirty bit for a given resource and tenant.
|
||||
def set_quota_usage_dirty(context, resource, project_id, dirty=True):
|
||||
"""Set quota usage dirty bit for a given resource and project.
|
||||
|
||||
:param resource: a resource for which quota usage if tracked
|
||||
:param tenant_id: tenant identifier
|
||||
:param project_id: project identifier
|
||||
:param dirty: the desired value for the dirty bit (defaults to True)
|
||||
:returns: 1 if the quota usage data were updated, 0 otherwise.
|
||||
"""
|
||||
obj = quota_obj.QuotaUsage.get_object(
|
||||
context, resource=resource, project_id=tenant_id)
|
||||
context, resource=resource, project_id=project_id)
|
||||
if obj:
|
||||
obj.dirty = dirty
|
||||
obj.update()
|
||||
@ -132,16 +131,17 @@ def set_quota_usage_dirty(context, resource, tenant_id, dirty=True):
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
@db_api.CONTEXT_WRITER
|
||||
def set_resources_quota_usage_dirty(context, resources, tenant_id, dirty=True):
|
||||
"""Set quota usage dirty bit for a given tenant and multiple resources.
|
||||
def set_resources_quota_usage_dirty(context, resources, project_id,
|
||||
dirty=True):
|
||||
"""Set quota usage dirty bit for a given project and multiple resources.
|
||||
|
||||
:param resources: list of resource for which the dirty bit is going
|
||||
to be set
|
||||
:param tenant_id: tenant identifier
|
||||
:param project_id: project identifier
|
||||
:param dirty: the desired value for the dirty bit (defaults to True)
|
||||
:returns: the number of records for which the bit was actually set.
|
||||
"""
|
||||
filters = {'project_id': tenant_id}
|
||||
filters = {'project_id': project_id}
|
||||
if resources:
|
||||
filters['resource'] = resources
|
||||
objs = quota_obj.QuotaUsage.get_objects(context, **filters)
|
||||
@ -154,10 +154,10 @@ def set_resources_quota_usage_dirty(context, resources, tenant_id, dirty=True):
|
||||
@db_api.retry_if_session_inactive()
|
||||
@db_api.CONTEXT_WRITER
|
||||
def set_all_quota_usage_dirty(context, resource, dirty=True):
|
||||
"""Set the dirty bit on quota usage for all tenants.
|
||||
"""Set the dirty bit on quota usage for all projects.
|
||||
|
||||
:param resource: the resource for which the dirty bit should be set
|
||||
:returns: the number of tenants for which the dirty bit was
|
||||
:returns: the number of projects for which the dirty bit was
|
||||
actually updated
|
||||
"""
|
||||
# TODO(manjeets) consider squashing this method with
|
||||
@ -170,7 +170,7 @@ def set_all_quota_usage_dirty(context, resource, dirty=True):
|
||||
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def create_reservation(context, tenant_id, deltas, expiration=None):
|
||||
def create_reservation(context, project_id, deltas, expiration=None):
|
||||
# This method is usually called from within another transaction.
|
||||
# Consider using begin_nested
|
||||
expiration = expiration or (utcnow() + datetime.timedelta(0, 120))
|
||||
@ -179,7 +179,7 @@ def create_reservation(context, tenant_id, deltas, expiration=None):
|
||||
delta_objs.append(quota_obj.ResourceDelta(
|
||||
context, resource=resource, amount=delta))
|
||||
reserv_obj = quota_obj.Reservation(
|
||||
context, project_id=tenant_id, expiration=expiration,
|
||||
context, project_id=project_id, expiration=expiration,
|
||||
resource_deltas=delta_objs)
|
||||
reserv_obj.create()
|
||||
return ReservationInfo(reserv_obj['id'],
|
||||
@ -223,12 +223,12 @@ def remove_reservation(context, reservation_id, set_dirty=False):
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
@db_api.CONTEXT_READER
|
||||
def get_reservations_for_resources(context, tenant_id, resources,
|
||||
def get_reservations_for_resources(context, project_id, resources,
|
||||
expired=False):
|
||||
"""Retrieve total amount of reservations for specified resources.
|
||||
|
||||
:param context: Neutron context with db session
|
||||
:param tenant_id: Tenant identifier
|
||||
:param project_id: Project identifier
|
||||
:param resources: Resources for which reserved amounts should be fetched
|
||||
:param expired: False to fetch active reservations, True to fetch expired
|
||||
reservations (defaults to False)
|
||||
@ -238,17 +238,17 @@ def get_reservations_for_resources(context, tenant_id, resources,
|
||||
# can be mocked easily where as datetime is built in type
|
||||
# mock.path does not allow mocking built in types.
|
||||
return quota_obj.Reservation.get_total_reservations_map(
|
||||
context, utcnow(), tenant_id, resources, expired)
|
||||
context, utcnow(), project_id, resources, expired)
|
||||
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
@db_api.CONTEXT_WRITER
|
||||
def remove_expired_reservations(context, tenant_id=None, timeout=None):
|
||||
def remove_expired_reservations(context, project_id=None, timeout=None):
|
||||
expiring_time = utcnow()
|
||||
if timeout:
|
||||
expiring_time -= datetime.timedelta(seconds=timeout)
|
||||
return quota_obj.Reservation.delete_expired(context, expiring_time,
|
||||
tenant_id)
|
||||
project_id)
|
||||
|
||||
|
||||
class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
||||
@ -257,7 +257,7 @@ class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def get_default_quotas(context, resources, project_id):
|
||||
"""Given a list of resources, retrieve the default quotas set for
|
||||
a tenant.
|
||||
a project.
|
||||
|
||||
:param context: The request context, for access checks.
|
||||
:param resources: A dictionary of the registered resource keys.
|
||||
@ -267,7 +267,7 @@ class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def get_tenant_quotas(context, resources, project_id):
|
||||
def get_project_quotas(context, resources, project_id):
|
||||
"""Retrieve the quotas for the given list of resources and project
|
||||
|
||||
:param context: The request context, for access checks.
|
||||
@ -278,7 +278,7 @@ class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def get_detailed_tenant_quotas(context, resources, project_id):
|
||||
def get_detailed_project_quotas(context, resources, project_id):
|
||||
"""Retrieve detailed quotas for the given list of resources and project
|
||||
|
||||
:param context: The request context, for access checks.
|
||||
@ -291,11 +291,11 @@ class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def delete_tenant_quota(context, project_id):
|
||||
def delete_project_quota(context, project_id):
|
||||
"""Delete the quota entries for a given project_id.
|
||||
|
||||
After deletion, this tenant will use default quota values in conf.
|
||||
Raise a "not found" error if the quota for the given tenant was
|
||||
After deletion, this project will use default quota values in conf.
|
||||
Raise a "not found" error if the quota for the given project was
|
||||
never defined.
|
||||
|
||||
:param context: The request context, for access checks.
|
||||
@ -382,15 +382,15 @@ class NullQuotaDriver(QuotaDriverAPI):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_tenant_quotas(context, resources, project_id):
|
||||
def get_project_quotas(context, resources, project_id):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_detailed_tenant_quotas(context, resources, project_id):
|
||||
def get_detailed_project_quotas(context, resources, project_id):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def delete_tenant_quota(context, project_id):
|
||||
def delete_project_quota(context, project_id):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
|
@ -35,139 +35,142 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_default_quotas(context, resources, tenant_id):
|
||||
def get_default_quotas(context, resources, project_id):
|
||||
"""Given a list of resources, retrieve the default quotas set for
|
||||
a tenant.
|
||||
a project.
|
||||
|
||||
:param context: The request context, for access checks.
|
||||
:param resources: A dictionary of the registered resource keys.
|
||||
:param tenant_id: The ID of the tenant to return default quotas for.
|
||||
:param project_id: The ID of the project to return default quotas for.
|
||||
:return: dict from resource name to dict of name and limit
|
||||
"""
|
||||
# Currently the tenant_id parameter is unused, since all tenants
|
||||
# Currently the project_id parameter is unused, since all projects
|
||||
# share the same default values. This may change in the future so
|
||||
# we include tenant-id to remain backwards compatible.
|
||||
# we include project ID to remain backwards compatible.
|
||||
return dict((key, resource.default)
|
||||
for key, resource in resources.items())
|
||||
|
||||
@staticmethod
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_tenant_quotas(context, resources, tenant_id):
|
||||
def get_project_quotas(context, resources, project_id):
|
||||
"""Given a list of resources, retrieve the quotas for the given
|
||||
tenant. If no limits are found for the specified tenant, the operation
|
||||
returns the default limits.
|
||||
project. If no limits are found for the specified project, the
|
||||
operation returns the default limits.
|
||||
|
||||
:param context: The request context, for access checks.
|
||||
:param resources: A dictionary of the registered resource keys.
|
||||
:param tenant_id: The ID of the tenant to return quotas for.
|
||||
:param project_id: The ID of the project to return quotas for.
|
||||
:return: dict from resource name to dict of name and limit
|
||||
"""
|
||||
|
||||
# init with defaults
|
||||
tenant_quota = dict((key, resource.default)
|
||||
project_quota = dict((key, resource.default)
|
||||
for key, resource in resources.items())
|
||||
|
||||
# update with tenant specific limits
|
||||
quota_objs = quota_obj.Quota.get_objects(context, project_id=tenant_id)
|
||||
# update with project specific limits
|
||||
quota_objs = quota_obj.Quota.get_objects(context,
|
||||
project_id=project_id)
|
||||
for item in quota_objs:
|
||||
tenant_quota[item['resource']] = item['limit']
|
||||
project_quota[item['resource']] = item['limit']
|
||||
|
||||
return tenant_quota
|
||||
return project_quota
|
||||
|
||||
@staticmethod
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_detailed_tenant_quotas(context, resources, tenant_id):
|
||||
"""Given a list of resources and a sepecific tenant, retrieve
|
||||
def get_detailed_project_quotas(context, resources, project_id):
|
||||
"""Given a list of resources and a specific project, retrieve
|
||||
the detailed quotas (limit, used, reserved).
|
||||
:param context: The request context, for access checks.
|
||||
:param resources: A dictionary of the registered resource keys.
|
||||
:param project_id: The ID of the project to return quotas for.
|
||||
:return dict: mapping resource name in dict to its corresponding limit
|
||||
used and reserved. Reserved currently returns default value of 0
|
||||
"""
|
||||
res_reserve_info = quota_api.get_reservations_for_resources(
|
||||
context, tenant_id, resources.keys())
|
||||
tenant_quota_ext = {}
|
||||
context, project_id, resources.keys())
|
||||
project_quota_ext = {}
|
||||
for key, resource in resources.items():
|
||||
if isinstance(resource, res.TrackedResource):
|
||||
used = resource.count_used(context, tenant_id,
|
||||
used = resource.count_used(context, project_id,
|
||||
resync_usage=False)
|
||||
else:
|
||||
# NOTE(ihrachys) .count won't use the plugin we pass, but we
|
||||
# pass it regardless to keep the quota driver API intact
|
||||
plugins = directory.get_plugins()
|
||||
plugin = plugins.get(key, plugins[constants.CORE])
|
||||
used = resource.count(context, plugin, tenant_id)
|
||||
used = resource.count(context, plugin, project_id)
|
||||
|
||||
tenant_quota_ext[key] = {
|
||||
project_quota_ext[key] = {
|
||||
'limit': resource.default,
|
||||
'used': used,
|
||||
'reserved': res_reserve_info.get(key, 0),
|
||||
}
|
||||
# update with specific tenant limits
|
||||
quota_objs = quota_obj.Quota.get_objects(context, project_id=tenant_id)
|
||||
# update with specific project limits
|
||||
quota_objs = quota_obj.Quota.get_objects(context,
|
||||
project_id=project_id)
|
||||
for item in quota_objs:
|
||||
tenant_quota_ext[item['resource']]['limit'] = item['limit']
|
||||
return tenant_quota_ext
|
||||
project_quota_ext[item['resource']]['limit'] = item['limit']
|
||||
return project_quota_ext
|
||||
|
||||
@staticmethod
|
||||
@db_api.retry_if_session_inactive()
|
||||
def delete_tenant_quota(context, tenant_id):
|
||||
"""Delete the quota entries for a given tenant_id.
|
||||
def delete_project_quota(context, project_id):
|
||||
"""Delete the quota entries for a given project_id.
|
||||
|
||||
After deletion, this tenant will use default quota values in conf.
|
||||
Raise a "not found" error if the quota for the given tenant was
|
||||
After deletion, this project will use default quota values in conf.
|
||||
Raise a "not found" error if the quota for the given project was
|
||||
never defined.
|
||||
"""
|
||||
if quota_obj.Quota.delete_objects(context, project_id=tenant_id) < 1:
|
||||
if quota_obj.Quota.delete_objects(context, project_id=project_id) < 1:
|
||||
# No record deleted means the quota was not found
|
||||
raise exceptions.TenantQuotaNotFound(tenant_id=tenant_id)
|
||||
raise exceptions.TenantQuotaNotFound(tenant_id=project_id)
|
||||
|
||||
@staticmethod
|
||||
@db_api.retry_if_session_inactive()
|
||||
def get_all_quotas(context, resources):
|
||||
"""Given a list of resources, retrieve the quotas for the all tenants.
|
||||
"""Given a list of resources, retrieve the quotas for the all projects.
|
||||
|
||||
:param context: The request context, for access checks.
|
||||
:param resources: A dictionary of the registered resource keys.
|
||||
:return: quotas list of dict of tenant_id:, resourcekey1:
|
||||
:return: quotas list of dict of project_id:, resourcekey1:
|
||||
resourcekey2: ...
|
||||
"""
|
||||
tenant_default = dict((key, resource.default)
|
||||
project_default = dict((key, resource.default)
|
||||
for key, resource in resources.items())
|
||||
|
||||
all_tenant_quotas = {}
|
||||
all_project_quotas = {}
|
||||
|
||||
for quota in quota_obj.Quota.get_objects(context):
|
||||
tenant_id = quota['project_id']
|
||||
project_id = quota['project_id']
|
||||
|
||||
# avoid setdefault() because only want to copy when actually
|
||||
# required
|
||||
tenant_quota = all_tenant_quotas.get(tenant_id)
|
||||
if tenant_quota is None:
|
||||
tenant_quota = tenant_default.copy()
|
||||
tenant_quota['tenant_id'] = tenant_id
|
||||
attributes.populate_project_info(tenant_quota)
|
||||
all_tenant_quotas[tenant_id] = tenant_quota
|
||||
project_quota = all_project_quotas.get(project_id)
|
||||
if project_quota is None:
|
||||
project_quota = project_default.copy()
|
||||
project_quota['project_id'] = project_id
|
||||
attributes.populate_project_info(project_quota)
|
||||
all_project_quotas[project_id] = project_quota
|
||||
|
||||
tenant_quota[quota['resource']] = quota['limit']
|
||||
project_quota[quota['resource']] = quota['limit']
|
||||
|
||||
# Convert values to a list to as caller expect an indexable iterable,
|
||||
# where python3's dict_values does not support indexing
|
||||
return list(all_tenant_quotas.values())
|
||||
return list(all_project_quotas.values())
|
||||
|
||||
@staticmethod
|
||||
@db_api.retry_if_session_inactive()
|
||||
def update_quota_limit(context, tenant_id, resource, limit):
|
||||
tenant_quotas = quota_obj.Quota.get_objects(
|
||||
context, project_id=tenant_id, resource=resource)
|
||||
if tenant_quotas:
|
||||
tenant_quotas[0].limit = limit
|
||||
tenant_quotas[0].update()
|
||||
def update_quota_limit(context, project_id, resource, limit):
|
||||
project_quotas = quota_obj.Quota.get_objects(
|
||||
context, project_id=project_id, resource=resource)
|
||||
if project_quotas:
|
||||
project_quotas[0].limit = limit
|
||||
project_quotas[0].update()
|
||||
else:
|
||||
quota_obj.Quota(context, project_id=tenant_id, resource=resource,
|
||||
quota_obj.Quota(context, project_id=project_id, resource=resource,
|
||||
limit=limit).create()
|
||||
|
||||
def _get_quotas(self, context, tenant_id, resources):
|
||||
def _get_quotas(self, context, project_id, resources):
|
||||
"""Retrieves the quotas for specific resources.
|
||||
|
||||
A helper method which retrieves the quotas for the specific
|
||||
@ -175,24 +178,23 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
||||
context.
|
||||
|
||||
:param context: The request context, for access checks.
|
||||
:param tenant_id: the tenant_id to check quota.
|
||||
:param project_id: the project ID to check quota.
|
||||
:param resources: A dictionary of the registered resources.
|
||||
"""
|
||||
# Grab and return the quotas (without usages)
|
||||
quotas = DbQuotaDriver.get_tenant_quotas(
|
||||
context, resources, tenant_id)
|
||||
quotas = DbQuotaDriver.get_project_quotas(
|
||||
context, resources, project_id)
|
||||
|
||||
return dict((k, v) for k, v in quotas.items())
|
||||
|
||||
def _handle_expired_reservations(self, context, tenant_id):
|
||||
LOG.debug("Deleting expired reservations for tenant:%s", tenant_id)
|
||||
def _handle_expired_reservations(self, context, project_id):
|
||||
LOG.debug("Deleting expired reservations for project: %s", project_id)
|
||||
# Delete expired reservations (we don't want them to accrue
|
||||
# in the database)
|
||||
quota_api.remove_expired_reservations(
|
||||
context, tenant_id=tenant_id)
|
||||
quota_api.remove_expired_reservations(context, project_id=project_id)
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def make_reservation(self, context, tenant_id, resources, deltas, plugin):
|
||||
def make_reservation(self, context, project_id, resources, deltas, plugin):
|
||||
# Lock current reservation table
|
||||
# NOTE(salv-orlando): This routine uses DB write locks.
|
||||
# These locks are acquired by the count() method invoked on resources.
|
||||
@ -207,11 +209,11 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
||||
# to a single node will be available.
|
||||
requested_resources = deltas.keys()
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
# get_tenant_quotes needs in input a dictionary mapping resource
|
||||
# "get_project_quotas" needs in input a dictionary mapping resource
|
||||
# name to BaseResosurce instances so that the default quota can be
|
||||
# retrieved
|
||||
current_limits = self.get_tenant_quotas(
|
||||
context, resources, tenant_id)
|
||||
current_limits = self.get_project_quotas(
|
||||
context, resources, project_id)
|
||||
unlimited_resources = set([resource for (resource, limit) in
|
||||
current_limits.items() if limit < 0])
|
||||
# Do not even bother counting resources and calculating headroom
|
||||
@ -230,13 +232,13 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
||||
# instances
|
||||
current_usages = dict(
|
||||
(resource, resources[resource].count(
|
||||
context, plugin, tenant_id, resync_usage=False)) for
|
||||
context, plugin, project_id, resync_usage=False)) for
|
||||
resource in requested_resources)
|
||||
# Adjust for expired reservations. Apparently it is cheaper than
|
||||
# querying every time for active reservations and counting overall
|
||||
# quantity of resources reserved
|
||||
expired_deltas = quota_api.get_reservations_for_resources(
|
||||
context, tenant_id, requested_resources, expired=True)
|
||||
context, project_id, requested_resources, expired=True)
|
||||
# Verify that the request can be accepted with current limits
|
||||
resources_over_limit = []
|
||||
for resource in requested_resources:
|
||||
@ -254,14 +256,14 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
||||
if res_headroom < deltas[resource]:
|
||||
resources_over_limit.append(resource)
|
||||
if expired_reservations:
|
||||
self._handle_expired_reservations(context, tenant_id)
|
||||
self._handle_expired_reservations(context, project_id)
|
||||
|
||||
if resources_over_limit:
|
||||
raise exceptions.OverQuota(overs=sorted(resources_over_limit))
|
||||
# Success, store the reservation
|
||||
# TODO(salv-orlando): Make expiration time configurable
|
||||
return quota_api.create_reservation(
|
||||
context, tenant_id, deltas)
|
||||
context, project_id, deltas)
|
||||
|
||||
def commit_reservation(self, context, reservation_id):
|
||||
# Do not mark resource usage as dirty. If a reservation is committed,
|
||||
@ -276,7 +278,7 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
||||
quota_api.remove_reservation(context, reservation_id,
|
||||
set_dirty=True)
|
||||
|
||||
def limit_check(self, context, tenant_id, resources, values):
|
||||
def limit_check(self, context, project_id, resources, values):
|
||||
"""Check simple quota limits.
|
||||
|
||||
For limits--those quotas for which there is no usage
|
||||
@ -289,7 +291,7 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
||||
nothing.
|
||||
|
||||
:param context: The request context, for access checks.
|
||||
:param tenant_id: The tenant_id to check the quota.
|
||||
:param project_id: The project ID to check the quota.
|
||||
:param resources: A dictionary of the registered resources.
|
||||
:param values: A dictionary of the values to check against the
|
||||
quota.
|
||||
@ -301,7 +303,7 @@ class DbQuotaDriver(quota_api.QuotaDriverAPI):
|
||||
raise exceptions.InvalidQuotaValue(unders=sorted(unders))
|
||||
|
||||
# Get the applicable quotas
|
||||
quotas = self._get_quotas(context, tenant_id, resources)
|
||||
quotas = self._get_quotas(context, project_id, resources)
|
||||
|
||||
# Check the quotas and construct a list of the resources that
|
||||
# would be put over limit by the desired values
|
||||
|
@ -45,7 +45,7 @@ class DbQuotaNoLockDriver(quota_driver.DbQuotaDriver):
|
||||
resources_over_limit = []
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
# Filter out unlimited resources.
|
||||
limits = self.get_tenant_quotas(context, resources, project_id)
|
||||
limits = self.get_project_quotas(context, resources, project_id)
|
||||
unlimited_resources = set([resource for (resource, limit) in
|
||||
limits.items() if limit < 0])
|
||||
requested_resources = (set(deltas.keys()) - unlimited_resources)
|
||||
@ -54,7 +54,7 @@ class DbQuotaNoLockDriver(quota_driver.DbQuotaDriver):
|
||||
# operation is fast and by calling it before making any
|
||||
# reservation, we ensure the freshness of the reservations.
|
||||
quota_api.remove_expired_reservations(
|
||||
context, tenant_id=project_id,
|
||||
context, project_id=project_id,
|
||||
timeout=quota_api.RESERVATION_EXPIRATION_TIMEOUT)
|
||||
|
||||
# Count the number of (1) used and (2) reserved resources for this
|
||||
|
@ -924,10 +924,10 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
@registry.receives(resources.NETWORK, [events.BEFORE_CREATE])
|
||||
def _ensure_default_security_group_handler(self, resource, event, trigger,
|
||||
payload):
|
||||
if event == events.BEFORE_UPDATE:
|
||||
project_id = payload.states[0]['tenant_id']
|
||||
else:
|
||||
project_id = payload.latest_state['tenant_id']
|
||||
_state = (payload.states[0] if event == events.BEFORE_UPDATE else
|
||||
payload.latest_state)
|
||||
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||
project_id = _state.get('project_id') or _state['tenant_id']
|
||||
if project_id:
|
||||
self._ensure_default_security_group(payload.context, project_id)
|
||||
|
||||
@ -993,7 +993,8 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
return
|
||||
port_sg = port.get(ext_sg.SECURITYGROUPS)
|
||||
if port_sg is None or not validators.is_attr_set(port_sg):
|
||||
port_project = port.get('tenant_id')
|
||||
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||
port_project = port.get('project_id') or port.get('tenant_id')
|
||||
default_sg = self._ensure_default_security_group(context,
|
||||
port_project)
|
||||
if default_sg:
|
||||
|
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import warnings
|
||||
|
||||
from neutron_lib.api import converters
|
||||
from neutron_lib.api import extensions as api_extensions
|
||||
from neutron_lib.api import faults
|
||||
@ -74,20 +76,20 @@ class QuotaSetsController(wsgi.Controller):
|
||||
'is_visible': True}
|
||||
self._update_extended_attributes = False
|
||||
|
||||
def _get_quotas(self, request, tenant_id):
|
||||
return self._driver.get_tenant_quotas(
|
||||
def _get_quotas(self, request, project_id):
|
||||
return self._driver.get_project_quotas(
|
||||
request.context,
|
||||
resource_registry.get_all_resources(),
|
||||
tenant_id)
|
||||
project_id)
|
||||
|
||||
def default(self, request, id):
|
||||
context = request.context
|
||||
if id != context.tenant_id:
|
||||
if id != context.project_id:
|
||||
validate_policy(context, "get_quota")
|
||||
return {self._resource_name: self._driver.get_default_quotas(
|
||||
context=context,
|
||||
resources=resource_registry.get_all_resources(),
|
||||
tenant_id=id)}
|
||||
project_id=id)}
|
||||
|
||||
def create(self, request, body=None):
|
||||
msg = _('POST requests are not supported on this resource.')
|
||||
@ -101,20 +103,31 @@ class QuotaSetsController(wsgi.Controller):
|
||||
context, resource_registry.get_all_resources())}
|
||||
|
||||
def tenant(self, request):
|
||||
"""Retrieve the tenant info in context."""
|
||||
"""Retrieve the project info in context."""
|
||||
warnings.warn(
|
||||
'"tenant" Quota API method is deprecated, use "project" instead')
|
||||
return self._project(request, 'tenant')
|
||||
|
||||
def project(self, request):
|
||||
"""Retrieve the project info in context."""
|
||||
return self._project(request, 'project')
|
||||
|
||||
@staticmethod
|
||||
def _project(request, key):
|
||||
"""Retrieve the project info in context."""
|
||||
context = request.context
|
||||
if not context.tenant_id:
|
||||
if not context.project_id:
|
||||
raise exceptions.QuotaMissingTenant()
|
||||
return {'tenant': {'tenant_id': context.tenant_id}}
|
||||
return {key: {key + '_id': context.project_id}}
|
||||
|
||||
def show(self, request, id):
|
||||
if id != request.context.tenant_id:
|
||||
if id != request.context.project_id:
|
||||
validate_policy(request.context, "get_quota")
|
||||
return {self._resource_name: self._get_quotas(request, id)}
|
||||
|
||||
def delete(self, request, id):
|
||||
validate_policy(request.context, "delete_quota")
|
||||
self._driver.delete_tenant_quota(request.context, id)
|
||||
self._driver.delete_project_quota(request.context, id)
|
||||
|
||||
def update(self, request, id, body=None):
|
||||
validate_policy(request.context, "update_quota")
|
||||
@ -152,7 +165,7 @@ class Quotasv2(api_extensions.ExtensionDescriptor):
|
||||
def get_description(cls):
|
||||
description = 'Expose functions for quotas management'
|
||||
if cfg.CONF.QUOTAS.quota_driver == DB_QUOTA_DRIVER:
|
||||
description += ' per tenant'
|
||||
description += ' per project'
|
||||
return description
|
||||
|
||||
@classmethod
|
||||
@ -169,7 +182,8 @@ class Quotasv2(api_extensions.ExtensionDescriptor):
|
||||
Quotasv2.get_alias(),
|
||||
controller,
|
||||
member_actions={DEFAULT_QUOTAS_ACTION: 'GET'},
|
||||
collection_actions={'tenant': 'GET'})]
|
||||
collection_actions={'tenant': 'GET',
|
||||
'project': 'GET'})]
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
|
@ -46,17 +46,17 @@ EXTENDED_ATTRIBUTES_2_0 = {
|
||||
|
||||
class DetailQuotaSetsController(quotasv2.QuotaSetsController):
|
||||
|
||||
def _get_detailed_quotas(self, request, tenant_id):
|
||||
return self._driver.get_detailed_tenant_quotas(
|
||||
def _get_detailed_quotas(self, request, project_id):
|
||||
return self._driver.get_detailed_project_quotas(
|
||||
request.context,
|
||||
resource_registry.get_all_resources(), tenant_id)
|
||||
resource_registry.get_all_resources(), project_id)
|
||||
|
||||
def details(self, request, id):
|
||||
if id != request.context.project_id:
|
||||
# Check if admin
|
||||
if not request.context.is_admin:
|
||||
reason = _("Only admin is authorized to access quotas for"
|
||||
" another tenant")
|
||||
reason = _("Only admin is authorized to access quotas for "
|
||||
"another project")
|
||||
raise n_exc.AdminRequired(reason=reason)
|
||||
return {self._resource_name:
|
||||
self._get_detailed_quotas(request, id)}
|
||||
@ -95,7 +95,8 @@ class Quotasv2_detail(api_extensions.ExtensionDescriptor):
|
||||
RESOURCE_COLLECTION,
|
||||
controller,
|
||||
member_actions={'details': 'GET'},
|
||||
collection_actions={'tenant': 'GET'})]
|
||||
collection_actions={'tenant': 'GET',
|
||||
'project': 'GET'})]
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
return EXTENDED_ATTRIBUTES_2_0 if version == "2.0" else {}
|
||||
|
@ -1051,7 +1051,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
def _create_network_db(self, context, network):
|
||||
net_data = network[net_def.RESOURCE_NAME]
|
||||
tenant_id = net_data['tenant_id']
|
||||
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||
tenant_id = net_data.get('project_id') or net_data['tenant_id']
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
net_db = self.create_network_db(context, network)
|
||||
net_data['id'] = net_db.id
|
||||
|
@ -30,7 +30,7 @@ from neutron.db.quota import api as quota_api
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def _count_resource(context, collection_name, tenant_id):
|
||||
def _count_resource(context, collection_name, project_id):
|
||||
count_getter_name = "get_%s_count" % collection_name
|
||||
getter_name = "get_%s" % collection_name
|
||||
|
||||
@ -45,12 +45,12 @@ def _count_resource(context, collection_name, tenant_id):
|
||||
try:
|
||||
obj_count_getter = getattr(plugins[pname], count_getter_name)
|
||||
return obj_count_getter(
|
||||
context, filters={'tenant_id': [tenant_id]})
|
||||
context, filters={'project_id': [project_id]})
|
||||
except (NotImplementedError, AttributeError):
|
||||
try:
|
||||
obj_getter = getattr(plugins[pname], getter_name)
|
||||
obj_list = obj_getter(
|
||||
context, filters={'tenant_id': [tenant_id]})
|
||||
context, filters={'project_id': [project_id]})
|
||||
return len(obj_list) if obj_list else 0
|
||||
except (NotImplementedError, AttributeError):
|
||||
pass
|
||||
@ -172,7 +172,7 @@ class TrackedResource(BaseResource):
|
||||
usage data are employed when performing quota checks.
|
||||
|
||||
This class operates under the assumption that the model class
|
||||
describing the resource has a tenant identifier attribute.
|
||||
describing the resource has a project identifier attribute.
|
||||
|
||||
:param name: The name of the resource, i.e., "networks".
|
||||
:param model_class: The sqlalchemy model class of the resource for
|
||||
@ -191,11 +191,11 @@ class TrackedResource(BaseResource):
|
||||
super(TrackedResource, self).__init__(
|
||||
name, flag=flag, plural_name=plural_name)
|
||||
# Register events for addition/removal of records in the model class
|
||||
# As tenant_id is immutable for all Neutron objects there is no need
|
||||
# As project_id is immutable for all Neutron objects there is no need
|
||||
# to register a listener for update events
|
||||
self._model_class = model_class
|
||||
self._dirty_tenants = set()
|
||||
self._out_of_sync_tenants = set()
|
||||
self._dirty_projects = set()
|
||||
self._out_of_sync_projects = set()
|
||||
# NOTE(ralonsoh): "DbQuotaNoLockDriver" driver does not need to track
|
||||
# the DB events or resync the resource quota usage.
|
||||
if cfg.CONF.QUOTAS.quota_driver == quota_conf.QUOTA_DB_DRIVER:
|
||||
@ -207,78 +207,78 @@ class TrackedResource(BaseResource):
|
||||
def dirty(self):
|
||||
if not self._track_resource_events:
|
||||
return
|
||||
return self._dirty_tenants
|
||||
return self._dirty_projects
|
||||
|
||||
def mark_dirty(self, context):
|
||||
if not self._dirty_tenants or not self._track_resource_events:
|
||||
if not self._dirty_projects or not self._track_resource_events:
|
||||
return
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
# It is not necessary to protect this operation with a lock.
|
||||
# Indeed when this method is called the request has been processed
|
||||
# and therefore all resources created or deleted.
|
||||
# dirty_tenants will contain all the tenants for which the
|
||||
# resource count is changed. The list might contain also tenants
|
||||
# dirty_projects will contain all the projects for which the
|
||||
# resource count is changed. The list might contain also projects
|
||||
# for which resource count was altered in other requests, but this
|
||||
# won't be harmful.
|
||||
dirty_tenants_snap = self._dirty_tenants.copy()
|
||||
for tenant_id in dirty_tenants_snap:
|
||||
quota_api.set_quota_usage_dirty(context, self.name, tenant_id)
|
||||
self._out_of_sync_tenants |= dirty_tenants_snap
|
||||
self._dirty_tenants -= dirty_tenants_snap
|
||||
dirty_projects_snap = self._dirty_projects.copy()
|
||||
for project_id in dirty_projects_snap:
|
||||
quota_api.set_quota_usage_dirty(context, self.name, project_id)
|
||||
self._out_of_sync_projects |= dirty_projects_snap
|
||||
self._dirty_projects -= dirty_projects_snap
|
||||
|
||||
def _db_event_handler(self, mapper, _conn, target):
|
||||
try:
|
||||
tenant_id = target['tenant_id']
|
||||
project_id = target['project_id']
|
||||
except AttributeError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Model class %s does not have a tenant_id "
|
||||
LOG.error("Model class %s does not have a project_id "
|
||||
"attribute", target)
|
||||
self._dirty_tenants.add(tenant_id)
|
||||
self._dirty_projects.add(project_id)
|
||||
|
||||
# Retry the operation if a duplicate entry exception is raised. This
|
||||
# can happen is two or more workers are trying to create a resource of a
|
||||
# give kind for the same tenant concurrently. Retrying the operation will
|
||||
# give kind for the same project concurrently. Retrying the operation will
|
||||
# ensure that an UPDATE statement is emitted rather than an INSERT one
|
||||
@db_api.retry_if_session_inactive()
|
||||
def _set_quota_usage(self, context, tenant_id, in_use):
|
||||
def _set_quota_usage(self, context, project_id, in_use):
|
||||
return quota_api.set_quota_usage(
|
||||
context, self.name, tenant_id, in_use=in_use)
|
||||
context, self.name, project_id, in_use=in_use)
|
||||
|
||||
def _resync(self, context, tenant_id, in_use):
|
||||
def _resync(self, context, project_id, in_use):
|
||||
# Update quota usage
|
||||
usage_info = self._set_quota_usage(context, tenant_id, in_use)
|
||||
usage_info = self._set_quota_usage(context, project_id, in_use)
|
||||
|
||||
self._dirty_tenants.discard(tenant_id)
|
||||
self._out_of_sync_tenants.discard(tenant_id)
|
||||
LOG.debug(("Unset dirty status for tenant:%(tenant_id)s on "
|
||||
self._dirty_projects.discard(project_id)
|
||||
self._out_of_sync_projects.discard(project_id)
|
||||
LOG.debug(("Unset dirty status for project:%(project_id)s on "
|
||||
"resource:%(resource)s"),
|
||||
{'tenant_id': tenant_id, 'resource': self.name})
|
||||
{'project_id': project_id, 'resource': self.name})
|
||||
return usage_info
|
||||
|
||||
def resync(self, context, tenant_id):
|
||||
if (tenant_id not in self._out_of_sync_tenants or
|
||||
def resync(self, context, project_id):
|
||||
if (project_id not in self._out_of_sync_projects or
|
||||
not self._track_resource_events):
|
||||
return
|
||||
LOG.debug(("Synchronizing usage tracker for tenant:%(tenant_id)s on "
|
||||
LOG.debug(("Synchronizing usage tracker for project:%(project_id)s on "
|
||||
"resource:%(resource)s"),
|
||||
{'tenant_id': tenant_id, 'resource': self.name})
|
||||
{'project_id': project_id, 'resource': self.name})
|
||||
in_use = context.session.query(
|
||||
self._model_class.tenant_id).filter_by(
|
||||
tenant_id=tenant_id).count()
|
||||
self._model_class.project_id).filter_by(
|
||||
project_id=project_id).count()
|
||||
# Update quota usage
|
||||
return self._resync(context, tenant_id, in_use)
|
||||
return self._resync(context, project_id, in_use)
|
||||
|
||||
def count_used(self, context, tenant_id, resync_usage=True):
|
||||
def count_used(self, context, project_id, resync_usage=True):
|
||||
"""Returns the current usage count for the resource.
|
||||
|
||||
:param context: The request context.
|
||||
:param tenant_id: The ID of the tenant
|
||||
:param project_id: The ID of the project
|
||||
:param resync_usage: Default value is set to True. Syncs
|
||||
with in_use usage.
|
||||
"""
|
||||
# Load current usage data, setting a row-level lock on the DB
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
context, self.name, tenant_id)
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||
context, self.name, project_id)
|
||||
|
||||
# If dirty or missing, calculate actual resource usage querying
|
||||
# the database and set/create usage info data
|
||||
@ -286,27 +286,28 @@ class TrackedResource(BaseResource):
|
||||
# assumption is generally valid, but if the database is tampered with,
|
||||
# or if data migrations do not take care of usage counters, the
|
||||
# assumption will not hold anymore
|
||||
if (tenant_id in self._dirty_tenants or
|
||||
if (project_id in self._dirty_projects or
|
||||
not usage_info or usage_info.dirty):
|
||||
LOG.debug(("Usage tracker for resource:%(resource)s and tenant:"
|
||||
"%(tenant_id)s is out of sync, need to count used "
|
||||
LOG.debug(("Usage tracker for resource:%(resource)s and project:"
|
||||
"%(project_id)s is out of sync, need to count used "
|
||||
"quota"), {'resource': self.name,
|
||||
'tenant_id': tenant_id})
|
||||
'project_id': project_id})
|
||||
in_use = context.session.query(
|
||||
self._model_class.tenant_id).filter_by(
|
||||
tenant_id=tenant_id).count()
|
||||
self._model_class.project_id).filter_by(
|
||||
project_id=project_id).count()
|
||||
|
||||
# Update quota usage, if requested (by default do not do that, as
|
||||
# typically one counts before adding a record, and that would mark
|
||||
# the usage counter as dirty again)
|
||||
if resync_usage:
|
||||
usage_info = self._resync(context, tenant_id, in_use)
|
||||
usage_info = self._resync(context, project_id, in_use)
|
||||
else:
|
||||
resource = usage_info.resource if usage_info else self.name
|
||||
tenant_id = usage_info.tenant_id if usage_info else tenant_id
|
||||
project_id = (usage_info.project_id if usage_info else
|
||||
project_id)
|
||||
dirty = usage_info.dirty if usage_info else True
|
||||
usage_info = quota_api.QuotaUsageInfo(
|
||||
resource, tenant_id, in_use, dirty)
|
||||
resource, project_id, in_use, dirty)
|
||||
|
||||
LOG.debug(("Quota usage for %(resource)s was recalculated. "
|
||||
"Used quota:%(used)d."),
|
||||
@ -314,16 +315,16 @@ class TrackedResource(BaseResource):
|
||||
'used': usage_info.used})
|
||||
return usage_info.used
|
||||
|
||||
def count_reserved(self, context, tenant_id):
|
||||
def count_reserved(self, context, project_id):
|
||||
"""Return the current reservation count for the resource."""
|
||||
# NOTE(princenana) Current implementation of reservations
|
||||
# is ephemeral and returns the default value
|
||||
reservations = quota_api.get_reservations_for_resources(
|
||||
context, tenant_id, [self.name])
|
||||
context, project_id, [self.name])
|
||||
reserved = reservations.get(self.name, 0)
|
||||
return reserved
|
||||
|
||||
def count(self, context, _plugin, tenant_id, resync_usage=True,
|
||||
def count(self, context, _plugin, project_id, resync_usage=True,
|
||||
count_db_registers=False):
|
||||
"""Return the count of the resource.
|
||||
|
||||
@ -332,11 +333,11 @@ class TrackedResource(BaseResource):
|
||||
CountableResource instances.
|
||||
"""
|
||||
if count_db_registers:
|
||||
count = self._count_db_registers(context, tenant_id)
|
||||
count = self._count_db_registers(context, project_id)
|
||||
else:
|
||||
count = self.count_used(context, tenant_id, resync_usage)
|
||||
count = self.count_used(context, project_id, resync_usage)
|
||||
|
||||
return count + self.count_reserved(context, tenant_id)
|
||||
return count + self.count_reserved(context, project_id)
|
||||
|
||||
def _count_db_registers(self, context, project_id):
|
||||
"""Return the existing resources (self._model_class) in a project.
|
||||
|
@ -192,7 +192,7 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
||||
def test_index_admin(self):
|
||||
# NOTE(salv-orlando): The quota controller has an hardcoded check for
|
||||
# admin-ness for this operation, which is supposed to return quotas for
|
||||
# all tenants. Such check is "vestigial" from the home-grown WSGI and
|
||||
# all projects. Such check is "vestigial" from the home-grown WSGI and
|
||||
# shall be removed
|
||||
response = self.app.get('%s.json' % self.base_url,
|
||||
headers={'X-Project-Id': 'admin',
|
||||
@ -213,7 +213,7 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
||||
self._verify_default_limits(json_body)
|
||||
|
||||
def test_get(self):
|
||||
# It is not ok to access another tenant's limits
|
||||
# It is not ok to access another project's limits
|
||||
url = '%s/foo.json' % self.base_url
|
||||
response = self.app.get(url, expect_errors=True)
|
||||
self.assertEqual(403, response.status_int)
|
||||
@ -269,7 +269,7 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
||||
json_body = jsonutils.loads(response.body)
|
||||
found = False
|
||||
for qs in json_body['quotas']:
|
||||
if qs['tenant_id'] == 'foo':
|
||||
if qs['project_id'] == 'foo':
|
||||
found = True
|
||||
self.assertTrue(found)
|
||||
|
||||
@ -283,7 +283,7 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
||||
self.assertEqual(200, response.status_int)
|
||||
json_body = jsonutils.loads(response.body)
|
||||
for qs in json_body['quotas']:
|
||||
self.assertNotEqual('foo', qs['tenant_id'])
|
||||
self.assertNotEqual('foo', qs['project_id'])
|
||||
|
||||
def test_quotas_get_defaults(self):
|
||||
response = self.app.get('%s/foo/default.json' % self.base_url,
|
||||
@ -294,13 +294,14 @@ class TestQuotasController(test_functional.PecanFunctionalTest):
|
||||
json_body = jsonutils.loads(response.body)
|
||||
self._verify_default_limits(json_body)
|
||||
|
||||
def test_get_tenant_info(self):
|
||||
response = self.app.get('%s/tenant.json' % self.base_url,
|
||||
headers={'X-Project-Id': 'admin',
|
||||
'X-Roles': 'admin'})
|
||||
self.assertEqual(200, response.status_int)
|
||||
json_body = jsonutils.loads(response.body)
|
||||
self.assertEqual('admin', json_body['tenant']['tenant_id'])
|
||||
def test_get_project_info(self):
|
||||
for key in ('project', 'tenant'):
|
||||
response = self.app.get('%s/%s.json' % (self.base_url, key),
|
||||
headers={'X-Project-Id': 'admin',
|
||||
'X-Roles': 'admin'})
|
||||
self.assertEqual(200, response.status_int)
|
||||
json_body = jsonutils.loads(response.body)
|
||||
self.assertEqual('admin', json_body[key][key + '_id'])
|
||||
|
||||
|
||||
class TestResourceController(TestRootController):
|
||||
@ -318,11 +319,11 @@ class TestResourceController(TestRootController):
|
||||
def _gen_port(self):
|
||||
network_id = self.plugin.create_network(context.get_admin_context(), {
|
||||
'network':
|
||||
{'name': 'pecannet', 'tenant_id': 'tenid', 'shared': False,
|
||||
{'name': 'pecannet', 'project_id': 'tenid', 'shared': False,
|
||||
'admin_state_up': True, 'status': 'ACTIVE'}})['id']
|
||||
self.port = self.plugin.create_port(context.get_admin_context(), {
|
||||
'port':
|
||||
{'tenant_id': 'tenid', 'network_id': network_id,
|
||||
{'project_id': 'tenid', 'network_id': network_id,
|
||||
'fixed_ips': n_const.ATTR_NOT_SPECIFIED,
|
||||
'mac_address': '00:11:22:33:44:55',
|
||||
'admin_state_up': True, 'device_id': 'FF',
|
||||
@ -364,7 +365,7 @@ class TestResourceController(TestRootController):
|
||||
self._test_get_collection_with_fields_selector(fields=[])
|
||||
|
||||
def test_project_id_in_mandatory_fields(self):
|
||||
# ports only specifies that tenant_id is mandatory, but project_id
|
||||
# ports only specifies that project_id is mandatory, but project_id
|
||||
# should still be passed to the plugin.
|
||||
mock_get = mock.patch.object(self.plugin, 'get_ports',
|
||||
return_value=[]).start()
|
||||
@ -384,10 +385,10 @@ class TestResourceController(TestRootController):
|
||||
# Explicitly require an attribute which is also 'required_by_policy'.
|
||||
# The attribute should not be stripped while generating the response
|
||||
item_resp = self.app.get(
|
||||
'/v2.0/ports/%s.json?fields=id&fields=tenant_id' % self.port['id'],
|
||||
headers={'X-Project-Id': 'tenid'})
|
||||
'/v2.0/ports/%s.json?fields=id&fields=project_id' %
|
||||
self.port['id'], headers={'X-Project-Id': 'tenid'})
|
||||
self.assertEqual(200, item_resp.status_int)
|
||||
self._check_item(['id', 'tenant_id'],
|
||||
self._check_item(['id', 'project_id'],
|
||||
jsonutils.loads(item_resp.body)['port'])
|
||||
|
||||
def test_duped_and_empty_fields_stripped(self):
|
||||
@ -406,7 +407,7 @@ class TestResourceController(TestRootController):
|
||||
'/v2.0/ports.json',
|
||||
params={'port': {'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'}},
|
||||
'project_id': 'tenid'}},
|
||||
headers={'X-Project-Id': 'tenid'})
|
||||
self.assertEqual(response.status_int, 201)
|
||||
|
||||
@ -426,7 +427,7 @@ class TestResourceController(TestRootController):
|
||||
'/v2.0/ports.json',
|
||||
params={'port': {'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'}},
|
||||
'project_id': 'tenid'}},
|
||||
headers={'X-Project-Id': 'tenid'})
|
||||
self.assertEqual(201, response.status_int)
|
||||
|
||||
@ -439,7 +440,7 @@ class TestResourceController(TestRootController):
|
||||
self.assertEqual(1, len(json_body))
|
||||
self.assertIn('port', json_body)
|
||||
self.assertEqual('test', json_body['port']['name'])
|
||||
self.assertEqual('tenid', json_body['port']['tenant_id'])
|
||||
self.assertEqual('tenid', json_body['port']['project_id'])
|
||||
|
||||
def test_delete(self):
|
||||
response = self.app.delete('/v2.0/ports/%s.json' % self.port['id'],
|
||||
@ -487,10 +488,10 @@ class TestResourceController(TestRootController):
|
||||
'/v2.0/ports.json',
|
||||
params={'ports': [{'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'},
|
||||
'project_id': 'tenid'},
|
||||
{'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'}]
|
||||
'project_id': 'tenid'}]
|
||||
},
|
||||
headers={'X-Project-Id': 'tenid'})
|
||||
self.assertEqual(201, response.status_int)
|
||||
@ -518,11 +519,11 @@ class TestResourceController(TestRootController):
|
||||
params={'ports': [{'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'security_groups': [sg_id],
|
||||
'tenant_id': 'tenid'},
|
||||
'project_id': 'tenid'},
|
||||
{'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'security_groups': [sg_id],
|
||||
'tenant_id': 'tenid'}]
|
||||
'project_id': 'tenid'}]
|
||||
},
|
||||
headers={'X-Project-Id': 'tenid'})
|
||||
self.assertEqual(201, port_response.status_int)
|
||||
@ -539,10 +540,10 @@ class TestResourceController(TestRootController):
|
||||
'/v2.0/ports.json',
|
||||
params={'ports': [{'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'},
|
||||
'project_id': 'tenid'},
|
||||
{'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'}]
|
||||
'project_id': 'tenid'}]
|
||||
},
|
||||
headers={'X-Project-Id': 'tenid'})
|
||||
self.assertEqual(response.status_int, 201)
|
||||
@ -556,13 +557,13 @@ class TestResourceController(TestRootController):
|
||||
'/v2.0/ports.json',
|
||||
params={'ports': [{'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'},
|
||||
'project_id': 'tenid'},
|
||||
{'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'},
|
||||
'project_id': 'tenid'},
|
||||
{'network_id': 'bad_net_id',
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'}]
|
||||
'project_id': 'tenid'}]
|
||||
},
|
||||
headers={'X-Project-Id': 'tenid'},
|
||||
expect_errors=True)
|
||||
@ -579,7 +580,7 @@ class TestResourceController(TestRootController):
|
||||
'/v2.0/ports.json',
|
||||
params={'ports': [{'network_id': self.port['network_id'],
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenid'}]
|
||||
'project_id': 'tenid'}]
|
||||
},
|
||||
headers={'X-Project-Id': 'tenid'})
|
||||
self.assertEqual(response.status_int, 201)
|
||||
@ -598,13 +599,15 @@ class TestPaginationAndSorting(test_functional.PecanFunctionalTest):
|
||||
self.addCleanup(policy.reset)
|
||||
self.plugin = directory.get_plugin()
|
||||
self.ctx = context.get_admin_context()
|
||||
self._project_id = 'project_id1'
|
||||
self._create_networks(self.RESOURCE_COUNT)
|
||||
self.networks = self._get_collection()['networks']
|
||||
|
||||
def _create_networks(self, count=1):
|
||||
network_ids = []
|
||||
for index in range(count):
|
||||
network = {'name': 'pecannet-%d' % index, 'tenant_id': 'tenid',
|
||||
network = {'name': 'pecannet-%d' % index,
|
||||
'project_id': self._project_id,
|
||||
'shared': False, 'admin_state_up': True,
|
||||
'status': 'ACTIVE'}
|
||||
network_id = self.plugin.create_network(
|
||||
@ -632,7 +635,8 @@ class TestPaginationAndSorting(test_functional.PecanFunctionalTest):
|
||||
url = '/v2.0/%s.json' % collection
|
||||
if query_params:
|
||||
url = '%s?%s' % (url, '&'.join(query_params))
|
||||
list_resp = self.app.get(url, headers={'X-Project-Id': 'tenid'})
|
||||
list_resp = self.app.get(url,
|
||||
headers={'X-Project-Id': self._project_id})
|
||||
self.assertEqual(200, list_resp.status_int)
|
||||
return list_resp.json
|
||||
|
||||
@ -735,9 +739,9 @@ class TestRequestProcessing(TestRootController):
|
||||
|
||||
def test_context_set_in_request(self):
|
||||
self.app.get('/v2.0/ports.json',
|
||||
headers={'X-Project-Id': 'tenant_id'})
|
||||
self.assertEqual('tenant_id',
|
||||
self.captured_context['neutron_context'].tenant_id)
|
||||
headers={'X-Project-Id': 'project_id'})
|
||||
self.assertEqual('project_id',
|
||||
self.captured_context['neutron_context'].project_id)
|
||||
|
||||
def test_core_resource_identified(self):
|
||||
self.app.get('/v2.0/ports.json')
|
||||
@ -1169,4 +1173,3 @@ class TestExcludeAttributePolicy(test_functional.PecanFunctionalTest):
|
||||
json_body = jsonutils.loads(response.body)
|
||||
self.assertEqual(response.status_int, 200)
|
||||
self.assertEqual('tenid', json_body['network']['project_id'])
|
||||
self.assertEqual('tenid', json_body['network']['tenant_id'])
|
||||
|
@ -31,26 +31,26 @@ DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
|
||||
class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
|
||||
def _set_context(self):
|
||||
self.tenant_id = 'Higuain'
|
||||
self.context = context.Context('Gonzalo', self.tenant_id,
|
||||
self.project_id = 'Higuain'
|
||||
self.context = context.Context('Gonzalo', self.project_id,
|
||||
is_admin=False, is_advsvc=False)
|
||||
|
||||
def _create_reservation(self, resource_deltas,
|
||||
tenant_id=None, expiration=None):
|
||||
tenant_id = tenant_id or self.tenant_id
|
||||
project_id=None, expiration=None):
|
||||
project_id = project_id or self.project_id
|
||||
return quota_api.create_reservation(
|
||||
self.context, tenant_id, resource_deltas, expiration)
|
||||
self.context, project_id, resource_deltas, expiration)
|
||||
|
||||
def _create_quota_usage(self, resource, used, tenant_id=None):
|
||||
tenant_id = tenant_id or self.tenant_id
|
||||
def _create_quota_usage(self, resource, used, project_id=None):
|
||||
project_id = project_id or self.project_id
|
||||
return quota_api.set_quota_usage(context.get_admin_context(),
|
||||
resource, tenant_id, in_use=used)
|
||||
resource, project_id, in_use=used)
|
||||
|
||||
def _verify_quota_usage(self, usage_info,
|
||||
expected_resource=None,
|
||||
expected_used=None,
|
||||
expected_dirty=None):
|
||||
self.assertEqual(self.tenant_id, usage_info.tenant_id)
|
||||
self.assertEqual(self.project_id, usage_info.project_id)
|
||||
if expected_resource:
|
||||
self.assertEqual(expected_resource, usage_info.resource)
|
||||
if expected_dirty is not None:
|
||||
@ -75,12 +75,12 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
self._create_quota_usage('goals', 26)
|
||||
# Higuain scores a double
|
||||
usage_info_1 = quota_api.set_quota_usage(
|
||||
self.context, 'goals', self.tenant_id,
|
||||
self.context, 'goals', self.project_id,
|
||||
in_use=28)
|
||||
self._verify_quota_usage(usage_info_1,
|
||||
expected_used=28)
|
||||
usage_info_2 = quota_api.set_quota_usage(
|
||||
self.context, 'goals', self.tenant_id,
|
||||
self.context, 'goals', self.project_id,
|
||||
in_use=24)
|
||||
self._verify_quota_usage(usage_info_2,
|
||||
expected_used=24)
|
||||
@ -89,7 +89,7 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
self._create_quota_usage('goals', 26)
|
||||
# Higuain scores a double
|
||||
usage_info_1 = quota_api.set_quota_usage(
|
||||
self.context, 'goals', self.tenant_id,
|
||||
self.context, 'goals', self.project_id,
|
||||
in_use=2, delta=True)
|
||||
self._verify_quota_usage(usage_info_1,
|
||||
expected_used=28)
|
||||
@ -98,35 +98,36 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
self._create_quota_usage('goals', 26)
|
||||
# Higuain needs a shower after the match
|
||||
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
||||
self.context, 'goals', self.tenant_id))
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'goals', self.tenant_id)
|
||||
self.context, 'goals', self.project_id))
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'goals', self.project_id)
|
||||
self._verify_quota_usage(usage_info,
|
||||
expected_dirty=True)
|
||||
# Higuain is clean now
|
||||
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
||||
self.context, 'goals', self.tenant_id, dirty=False))
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'goals', self.tenant_id)
|
||||
self.context, 'goals', self.project_id, dirty=False))
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'goals', self.project_id)
|
||||
self._verify_quota_usage(usage_info,
|
||||
expected_dirty=False)
|
||||
|
||||
def test_set_dirty_non_existing_quota_usage(self):
|
||||
self.assertEqual(0, quota_api.set_quota_usage_dirty(
|
||||
self.context, 'meh', self.tenant_id))
|
||||
self.context, 'meh', self.project_id))
|
||||
|
||||
def test_set_resources_quota_usage_dirty(self):
|
||||
self._create_quota_usage('goals', 26)
|
||||
self._create_quota_usage('assists', 11)
|
||||
self._create_quota_usage('bookings', 3)
|
||||
self.assertEqual(2, quota_api.set_resources_quota_usage_dirty(
|
||||
self.context, ['goals', 'bookings'], self.tenant_id))
|
||||
usage_info_goals = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'goals', self.tenant_id)
|
||||
usage_info_assists = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'assists', self.tenant_id)
|
||||
usage_info_bookings = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'bookings', self.tenant_id)
|
||||
self.context, ['goals', 'bookings'], self.project_id))
|
||||
usage_info_goals = quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'goals', self.project_id)
|
||||
usage_info_assists = quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'assists', self.project_id)
|
||||
usage_info_bookings = (
|
||||
quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'bookings', self.project_id))
|
||||
self._verify_quota_usage(usage_info_goals, expected_dirty=True)
|
||||
self._verify_quota_usage(usage_info_assists, expected_dirty=False)
|
||||
self._verify_quota_usage(usage_info_bookings, expected_dirty=True)
|
||||
@ -135,30 +136,31 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
self._create_quota_usage('goals', 26)
|
||||
self._create_quota_usage('assists', 11)
|
||||
self._create_quota_usage('bookings', 3)
|
||||
# Expect all the resources for the tenant to be set dirty
|
||||
# Expect all the resources for the project to be set dirty
|
||||
self.assertEqual(3, quota_api.set_resources_quota_usage_dirty(
|
||||
self.context, [], self.tenant_id))
|
||||
usage_info_goals = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'goals', self.tenant_id)
|
||||
usage_info_assists = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'assists', self.tenant_id)
|
||||
usage_info_bookings = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'bookings', self.tenant_id)
|
||||
self.context, [], self.project_id))
|
||||
usage_info_goals = quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'goals', self.project_id)
|
||||
usage_info_assists = quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'assists', self.project_id)
|
||||
usage_info_bookings = (
|
||||
quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'bookings', self.project_id))
|
||||
self._verify_quota_usage(usage_info_goals, expected_dirty=True)
|
||||
self._verify_quota_usage(usage_info_assists, expected_dirty=True)
|
||||
self._verify_quota_usage(usage_info_bookings, expected_dirty=True)
|
||||
|
||||
# Higuain is clean now
|
||||
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
||||
self.context, 'goals', self.tenant_id, dirty=False))
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'goals', self.tenant_id)
|
||||
self.context, 'goals', self.project_id, dirty=False))
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'goals', self.project_id)
|
||||
self._verify_quota_usage(usage_info,
|
||||
expected_dirty=False)
|
||||
|
||||
def _test_set_all_quota_usage_dirty(self, expected):
|
||||
self._create_quota_usage('goals', 26)
|
||||
self._create_quota_usage('goals', 12, tenant_id='Callejon')
|
||||
self._create_quota_usage('goals', 12, project_id='Callejon')
|
||||
self.assertEqual(expected, quota_api.set_all_quota_usage_dirty(
|
||||
self.context, 'goals'))
|
||||
|
||||
@ -167,13 +169,13 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
# admin context we can clean only one
|
||||
self._test_set_all_quota_usage_dirty(expected=1)
|
||||
|
||||
def test_get_quota_usage_by_tenant(self):
|
||||
def test_get_quota_usage_by_project(self):
|
||||
self._create_quota_usage('goals', 26)
|
||||
self._create_quota_usage('assists', 11)
|
||||
# Create a resource for a different tenant
|
||||
self._create_quota_usage('mehs', 99, tenant_id='buffon')
|
||||
usage_infos = quota_api.get_quota_usage_by_tenant_id(
|
||||
self.context, self.tenant_id)
|
||||
# Create a resource for a different project
|
||||
self._create_quota_usage('mehs', 99, project_id='buffon')
|
||||
usage_infos = quota_api.get_quota_usage_by_project_id(
|
||||
self.context, self.project_id)
|
||||
|
||||
self.assertEqual(2, len(usage_infos))
|
||||
resources = [info.resource for info in usage_infos]
|
||||
@ -183,26 +185,26 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
def test_get_quota_usage_by_resource(self):
|
||||
self._create_quota_usage('goals', 26)
|
||||
self._create_quota_usage('assists', 11)
|
||||
self._create_quota_usage('goals', 12, tenant_id='Callejon')
|
||||
self._create_quota_usage('goals', 12, project_id='Callejon')
|
||||
usage_infos = quota_api.get_quota_usage_by_resource(
|
||||
self.context, 'goals')
|
||||
# Only 1 result expected in tenant context
|
||||
# Only 1 result expected in project context
|
||||
self.assertEqual(1, len(usage_infos))
|
||||
self._verify_quota_usage(usage_infos[0],
|
||||
expected_resource='goals',
|
||||
expected_used=26)
|
||||
|
||||
def test_get_quota_usage_by_tenant_and_resource(self):
|
||||
def test_get_quota_usage_by_project_and_resource(self):
|
||||
self._create_quota_usage('goals', 26)
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'goals', self.tenant_id)
|
||||
usage_info = quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'goals', self.project_id)
|
||||
self._verify_quota_usage(usage_info,
|
||||
expected_resource='goals',
|
||||
expected_used=26)
|
||||
|
||||
def test_get_non_existing_quota_usage_returns_none(self):
|
||||
self.assertIsNone(quota_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.context, 'goals', self.tenant_id))
|
||||
self.assertIsNone(quota_api.get_quota_usage_by_resource_and_project(
|
||||
self.context, 'goals', self.project_id))
|
||||
|
||||
def _verify_reserved_resources(self, expected, actual):
|
||||
for (resource, delta) in actual.items():
|
||||
@ -214,14 +216,14 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
def test_create_reservation(self):
|
||||
resources = {'goals': 2, 'assists': 1}
|
||||
resv = self._create_reservation(resources)
|
||||
self.assertEqual(self.tenant_id, resv.tenant_id)
|
||||
self.assertEqual(self.project_id, resv.project_id)
|
||||
self._verify_reserved_resources(resources, resv.deltas)
|
||||
|
||||
def test_create_reservation_with_expiration(self):
|
||||
resources = {'goals': 2, 'assists': 1}
|
||||
exp_date = datetime.datetime(2016, 3, 31, 14, 30)
|
||||
resv = self._create_reservation(resources, expiration=exp_date)
|
||||
self.assertEqual(self.tenant_id, resv.tenant_id)
|
||||
self.assertEqual(self.project_id, resv.project_id)
|
||||
self.assertEqual(exp_date, resv.expiration)
|
||||
self._verify_reserved_resources(resources, resv.deltas)
|
||||
|
||||
@ -245,7 +247,8 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
mock_utcnow.return_value = datetime.datetime(
|
||||
2015, 5, 20, 0, 0)
|
||||
deltas = quota_api.get_reservations_for_resources(
|
||||
self.context, self.tenant_id, ['goals', 'assists', 'bookings'])
|
||||
self.context, self.project_id,
|
||||
['goals', 'assists', 'bookings'])
|
||||
self.assertIn('goals', deltas)
|
||||
self.assertEqual(5, deltas['goals'])
|
||||
self.assertIn('assists', deltas)
|
||||
@ -260,7 +263,7 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
2015, 5, 20, 0, 0)
|
||||
self._get_reservations_for_resource_helper()
|
||||
deltas = quota_api.get_reservations_for_resources(
|
||||
self.context, self.tenant_id,
|
||||
self.context, self.project_id,
|
||||
['goals', 'assists', 'bookings'],
|
||||
expired=True)
|
||||
self.assertIn('assists', deltas)
|
||||
@ -271,7 +274,7 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
|
||||
def test_get_reservation_for_resources_with_empty_list(self):
|
||||
self.assertIsNone(quota_api.get_reservations_for_resources(
|
||||
self.context, self.tenant_id, []))
|
||||
self.context, self.project_id, []))
|
||||
|
||||
def test_remove_expired_reservations(self):
|
||||
with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow:
|
||||
@ -283,13 +286,13 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
exp_date_2 = datetime.datetime(2015, 3, 31, 14, 30)
|
||||
resv_2 = self._create_reservation(resources, expiration=exp_date_2)
|
||||
self.assertEqual(1, quota_api.remove_expired_reservations(
|
||||
self.context, self.tenant_id))
|
||||
self.context, self.project_id))
|
||||
self.assertIsNone(quota_api.get_reservation(
|
||||
self.context, resv_2.reservation_id))
|
||||
self.assertIsNotNone(quota_api.get_reservation(
|
||||
self.context, resv_1.reservation_id))
|
||||
|
||||
def test_remove_expired_reservations_no_tenant(self):
|
||||
def test_remove_expired_reservations_no_project(self):
|
||||
with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow:
|
||||
mock_utcnow.return_value = datetime.datetime(
|
||||
2015, 5, 20, 0, 0)
|
||||
@ -299,7 +302,7 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
exp_date_2 = datetime.datetime(2015, 3, 31, 14, 30)
|
||||
resv_2 = self._create_reservation(resources,
|
||||
expiration=exp_date_2,
|
||||
tenant_id='Callejon')
|
||||
project_id='Callejon')
|
||||
self.assertEqual(2, quota_api.remove_expired_reservations(
|
||||
context.get_admin_context()))
|
||||
self.assertIsNone(quota_api.get_reservation(
|
||||
@ -311,14 +314,14 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
||||
class TestQuotaDbApiAdminContext(TestQuotaDbApi):
|
||||
|
||||
def _set_context(self):
|
||||
self.tenant_id = 'Higuain'
|
||||
self.context = context.Context('Gonzalo', self.tenant_id,
|
||||
self.project_id = 'Higuain'
|
||||
self.context = context.Context('Gonzalo', self.project_id,
|
||||
is_admin=True, is_advsvc=True)
|
||||
|
||||
def test_get_quota_usage_by_resource(self):
|
||||
self._create_quota_usage('goals', 26)
|
||||
self._create_quota_usage('assists', 11)
|
||||
self._create_quota_usage('goals', 12, tenant_id='Callejon')
|
||||
self._create_quota_usage('goals', 12, project_id='Callejon')
|
||||
usage_infos = quota_api.get_quota_usage_by_resource(
|
||||
self.context, 'goals')
|
||||
# 2 results expected in admin context
|
||||
|
@ -28,7 +28,7 @@ from neutron.tests.unit import testlib_api
|
||||
DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
|
||||
|
||||
|
||||
def _count_resource(context, resource, tenant_id):
|
||||
def _count_resource(context, resource, project_id):
|
||||
"""A fake counting function to determine current used counts"""
|
||||
if resource[-1] == 's':
|
||||
resource = resource[:-1]
|
||||
@ -97,7 +97,8 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
defaults = {RESOURCE: TestResource(RESOURCE, 4)}
|
||||
|
||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||
quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT)
|
||||
quotas = self.plugin.get_project_quotas(self.context, defaults,
|
||||
PROJECT)
|
||||
self.assertEqual(2, quotas[RESOURCE])
|
||||
|
||||
def test_update_quota_limit(self):
|
||||
@ -105,15 +106,17 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
|
||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 3)
|
||||
quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT)
|
||||
quotas = self.plugin.get_project_quotas(self.context, defaults,
|
||||
PROJECT)
|
||||
self.assertEqual(3, quotas[RESOURCE])
|
||||
|
||||
def test_delete_tenant_quota_restores_default_limit(self):
|
||||
def test_delete_project_quota_restores_default_limit(self):
|
||||
defaults = {RESOURCE: TestResource(RESOURCE, 4)}
|
||||
|
||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||
self.plugin.delete_tenant_quota(self.context, PROJECT)
|
||||
quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT)
|
||||
self.plugin.delete_project_quota(self.context, PROJECT)
|
||||
quotas = self.plugin.get_project_quotas(self.context, defaults,
|
||||
PROJECT)
|
||||
self.assertEqual(4, quotas[RESOURCE])
|
||||
|
||||
def test_get_default_quotas(self):
|
||||
@ -123,20 +126,20 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
quotas = self.plugin.get_default_quotas(user_ctx, defaults, PROJECT)
|
||||
self.assertEqual(4, quotas[RESOURCE])
|
||||
|
||||
def test_get_tenant_quotas(self):
|
||||
def test_get_project_quotas(self):
|
||||
user_ctx = context.Context(user_id=PROJECT, tenant_id=PROJECT)
|
||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||
quotas = self.plugin.get_tenant_quotas(user_ctx, {}, PROJECT)
|
||||
quotas = self.plugin.get_project_quotas(user_ctx, {}, PROJECT)
|
||||
self.assertEqual(2, quotas[RESOURCE])
|
||||
|
||||
def test_get_tenant_quotas_different_tenant(self):
|
||||
def test_get_project_quotas_different_project(self):
|
||||
user_ctx = context.Context(user_id=PROJECT,
|
||||
tenant_id='another_project')
|
||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2)
|
||||
# It is appropriate to use assertFalse here as the expected return
|
||||
# value is an empty dict (the defaults passed in the statement below
|
||||
# after the request context)
|
||||
self.assertFalse(self.plugin.get_tenant_quotas(user_ctx, {}, PROJECT))
|
||||
self.assertFalse(self.plugin.get_project_quotas(user_ctx, {}, PROJECT))
|
||||
|
||||
def test_get_all_quotas(self):
|
||||
project_1 = 'prj_test_1'
|
||||
@ -151,14 +154,14 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
self.plugin.update_quota_limit(self.context, project_2, resource_2, 9)
|
||||
quotas = self.plugin.get_all_quotas(self.context, resources)
|
||||
|
||||
# Expect two tenants' quotas
|
||||
# Expect two projects' quotas
|
||||
self.assertEqual(2, len(quotas))
|
||||
# But not quotas for the same tenant twice
|
||||
self.assertNotEqual(quotas[0]['tenant_id'], quotas[1]['tenant_id'])
|
||||
# But not quotas for the same project twice
|
||||
self.assertNotEqual(quotas[0]['project_id'], quotas[1]['project_id'])
|
||||
|
||||
# Check the expected limits. The quotas can be in any order.
|
||||
for quota in quotas:
|
||||
project = quota['tenant_id']
|
||||
project = quota['project_id']
|
||||
self.assertIn(project, (project_1, project_2))
|
||||
if project == project_1:
|
||||
expected_limit_r1 = 7
|
||||
@ -208,15 +211,15 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
self.plugin.update_quota_limit(self.context, PROJECT, resource_name, 2)
|
||||
reservation = quota_driver.make_reservation(
|
||||
self.context,
|
||||
self.context.tenant_id,
|
||||
self.context.project_id,
|
||||
resources,
|
||||
deltas,
|
||||
self.plugin)
|
||||
self.assertIn(resource_name, reservation.deltas)
|
||||
self.assertEqual(deltas[resource_name],
|
||||
reservation.deltas[resource_name])
|
||||
self.assertEqual(self.context.tenant_id,
|
||||
reservation.tenant_id)
|
||||
self.assertEqual(self.context.project_id,
|
||||
reservation.project_id)
|
||||
|
||||
def test_make_reservation_single_resource(self):
|
||||
quota_driver = driver.DbQuotaDriver()
|
||||
@ -237,7 +240,7 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
self.plugin.update_quota_limit(self.context, PROJECT, ALT_RESOURCE, 2)
|
||||
reservation = quota_driver.make_reservation(
|
||||
self.context,
|
||||
self.context.tenant_id,
|
||||
self.context.project_id,
|
||||
resources,
|
||||
deltas,
|
||||
self.plugin)
|
||||
@ -245,8 +248,8 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
self.assertIn(ALT_RESOURCE, reservation.deltas)
|
||||
self.assertEqual(1, reservation.deltas[RESOURCE])
|
||||
self.assertEqual(2, reservation.deltas[ALT_RESOURCE])
|
||||
self.assertEqual(self.context.tenant_id,
|
||||
reservation.tenant_id)
|
||||
self.assertEqual(self.context.project_id,
|
||||
reservation.project_id)
|
||||
|
||||
def test_make_reservation_over_quota_fails(self):
|
||||
quota_driver = driver.DbQuotaDriver()
|
||||
@ -257,12 +260,12 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
self.assertRaises(exceptions.OverQuota,
|
||||
quota_driver.make_reservation,
|
||||
self.context,
|
||||
self.context.tenant_id,
|
||||
self.context.project_id,
|
||||
resources,
|
||||
deltas,
|
||||
self.plugin)
|
||||
|
||||
def test_get_detailed_tenant_quotas_resource(self):
|
||||
def test_get_detailed_project_quotas_resource(self):
|
||||
res = {RESOURCE: TestTrackedResource(RESOURCE, test_quota.MehModel)}
|
||||
|
||||
self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 6)
|
||||
@ -270,13 +273,13 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
quota_driver.make_reservation(self.context, PROJECT, res,
|
||||
{RESOURCE: 1}, self.plugin)
|
||||
quota_api.set_quota_usage(self.context, RESOURCE, PROJECT, 2)
|
||||
detailed_quota = self.plugin.get_detailed_tenant_quotas(self.context,
|
||||
res, PROJECT)
|
||||
detailed_quota = self.plugin.get_detailed_project_quotas(
|
||||
self.context, res, PROJECT)
|
||||
self.assertEqual(6, detailed_quota[RESOURCE]['limit'])
|
||||
self.assertEqual(2, detailed_quota[RESOURCE]['used'])
|
||||
self.assertEqual(1, detailed_quota[RESOURCE]['reserved'])
|
||||
|
||||
def test_get_detailed_tenant_quotas_multiple_resource(self):
|
||||
def test_get_detailed_project_quotas_multiple_resource(self):
|
||||
project_1 = 'prj_test_1'
|
||||
resource_1 = 'res_test_1'
|
||||
resource_2 = 'res_test_2'
|
||||
@ -295,9 +298,8 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase,
|
||||
|
||||
quota_api.set_quota_usage(self.context, resource_1, project_1, 2)
|
||||
quota_api.set_quota_usage(self.context, resource_2, project_1, 3)
|
||||
detailed_quota = self.plugin.get_detailed_tenant_quotas(self.context,
|
||||
resources,
|
||||
project_1)
|
||||
detailed_quota = self.plugin.get_detailed_project_quotas(
|
||||
self.context, resources, project_1)
|
||||
|
||||
self.assertEqual(6, detailed_quota[resource_1]['limit'])
|
||||
self.assertEqual(1, detailed_quota[resource_1]['reserved'])
|
||||
|
@ -73,9 +73,9 @@ class QuotaExtensionTestCase(testlib_api.WebTestCase):
|
||||
router.APIRouter()
|
||||
|
||||
def _test_quota_default_values(self, expected_values):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id)}
|
||||
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
quota = self.deserialize(res)
|
||||
for resource, expected_value in expected_values.items():
|
||||
@ -118,10 +118,10 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
||||
'extra1': qconf.DEFAULT_QUOTA})
|
||||
|
||||
def test_show_default_quotas_with_admin(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=True)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
||||
res = self.api.get(_get_path('quotas', id=project_id,
|
||||
action=DEFAULT_QUOTAS_ACTION,
|
||||
fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
@ -134,11 +134,11 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
||||
self.assertEqual(
|
||||
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||
|
||||
def test_show_default_quotas_with_owner_tenant(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
def test_show_default_quotas_with_owner_project(self):
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=False)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
||||
res = self.api.get(_get_path('quotas', id=project_id,
|
||||
action=DEFAULT_QUOTAS_ACTION,
|
||||
fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
@ -152,20 +152,20 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
||||
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||
|
||||
def test_show_default_quotas_without_admin_forbidden_returns_403(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=False)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
||||
res = self.api.get(_get_path('quotas', id=project_id,
|
||||
action=DEFAULT_QUOTAS_ACTION,
|
||||
fmt=self.fmt),
|
||||
extra_environ=env, expect_errors=True)
|
||||
self.assertEqual(403, res.status_int)
|
||||
|
||||
def test_show_quotas_with_admin(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=True)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
self.assertEqual(200, res.status_int)
|
||||
quota = self.deserialize(res)
|
||||
@ -177,18 +177,18 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
||||
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||
|
||||
def test_show_quotas_without_admin_forbidden_returns_403(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=False)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env, expect_errors=True)
|
||||
self.assertEqual(403, res.status_int)
|
||||
|
||||
def test_show_quotas_with_owner_tenant(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
def test_show_quotas_with_owner_project(self):
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=False)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
self.assertEqual(200, res.status_int)
|
||||
quota = self.deserialize(res)
|
||||
@ -200,8 +200,8 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
||||
qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||
|
||||
def test_list_quotas_with_admin(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
res = self.api.get(_get_path('quotas', fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
@ -210,93 +210,93 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
||||
self.assertEqual([], quota['quotas'])
|
||||
|
||||
def test_list_quotas_without_admin_forbidden_returns_403(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=False)}
|
||||
res = self.api.get(_get_path('quotas', fmt=self.fmt),
|
||||
extra_environ=env, expect_errors=True)
|
||||
self.assertEqual(403, res.status_int)
|
||||
|
||||
def test_update_quotas_without_admin_forbidden_returns_403(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=False)}
|
||||
quotas = {'quota': {'network': 100}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env,
|
||||
expect_errors=True)
|
||||
self.assertEqual(403, res.status_int)
|
||||
|
||||
def test_update_quotas_with_non_integer_returns_400(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
quotas = {'quota': {'network': 'abc'}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_quotas_with_negative_integer_returns_400(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
quotas = {'quota': {'network': -2}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_quotas_with_out_of_range_integer_returns_400(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
quotas = {'quota': {'network': constants.DB_INTEGER_MAX_VALUE + 1}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env,
|
||||
expect_errors=True)
|
||||
self.assertEqual(exc.HTTPBadRequest.code, res.status_int)
|
||||
|
||||
def test_update_quotas_to_unlimited(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
quotas = {'quota': {'network': -1}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env,
|
||||
expect_errors=False)
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_update_quotas_exceeding_current_limit(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
quotas = {'quota': {'network': 120}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env,
|
||||
expect_errors=False)
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_update_quotas_with_non_support_resource_returns_400(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
quotas = {'quota': {'abc': 100}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_quotas_with_admin(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=True)}
|
||||
quotas = {'quota': {'network': 100}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env)
|
||||
self.assertEqual(200, res.status_int)
|
||||
env2 = {'neutron.context': context.Context('', tenant_id)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
env2 = {'neutron.context': context.Context('', project_id)}
|
||||
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env2)
|
||||
quota = self.deserialize(res)
|
||||
self.assertEqual(100, quota['quota']['network'])
|
||||
@ -304,44 +304,44 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
||||
self.assertEqual(qconf.DEFAULT_QUOTA_PORT, quota['quota']['port'])
|
||||
|
||||
def test_update_attributes(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=True)}
|
||||
quotas = {'quota': {'extra1': 100}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env)
|
||||
self.assertEqual(200, res.status_int)
|
||||
env2 = {'neutron.context': context.Context('', tenant_id)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
env2 = {'neutron.context': context.Context('', project_id)}
|
||||
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env2)
|
||||
quota = self.deserialize(res)
|
||||
self.assertEqual(100, quota['quota']['extra1'])
|
||||
|
||||
def test_delete_quotas_with_admin(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=True)}
|
||||
# Create a quota to ensure we have something to delete
|
||||
quotas = {'quota': {'network': 100}}
|
||||
self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env)
|
||||
res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.delete(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
self.assertEqual(204, res.status_int)
|
||||
|
||||
def test_delete_quotas_without_admin_forbidden_returns_403(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=False)}
|
||||
res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.delete(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env, expect_errors=True)
|
||||
self.assertEqual(403, res.status_int)
|
||||
|
||||
def test_delete_quota_with_unknown_tenant_returns_404(self):
|
||||
tenant_id = 'idnotexist'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
def test_delete_quota_with_unknown_project_returns_404(self):
|
||||
project_id = 'idnotexist'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=True)}
|
||||
res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.delete(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env, expect_errors=True)
|
||||
self.assertEqual(exc.HTTPNotFound.code, res.status_int)
|
||||
|
||||
@ -353,44 +353,48 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
||||
pass
|
||||
|
||||
def test_quotas_limit_check(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
quotas = {'quota': {'network': 5}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id,
|
||||
res = self.api.put(_get_path('quotas', id=project_id,
|
||||
fmt=self.fmt),
|
||||
self.serialize(quotas), extra_environ=env)
|
||||
self.assertEqual(200, res.status_int)
|
||||
quota.QUOTAS.limit_check(context.Context('', tenant_id),
|
||||
tenant_id,
|
||||
quota.QUOTAS.limit_check(context.Context('', project_id),
|
||||
project_id,
|
||||
network=4)
|
||||
|
||||
def test_quotas_limit_check_with_invalid_quota_value(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
project_id = 'project_id1'
|
||||
with testtools.ExpectedException(exceptions.InvalidQuotaValue):
|
||||
quota.QUOTAS.limit_check(context.Context('', tenant_id),
|
||||
tenant_id,
|
||||
quota.QUOTAS.limit_check(context.Context('', project_id),
|
||||
project_id,
|
||||
network=-2)
|
||||
|
||||
def test_quotas_limit_check_with_not_registered_resource_fails(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
project_id = 'project_id1'
|
||||
self.assertRaises(exceptions.QuotaResourceUnknown,
|
||||
quota.QUOTAS.limit_check,
|
||||
context.get_admin_context(),
|
||||
tenant_id,
|
||||
project_id,
|
||||
foobar=1)
|
||||
|
||||
def test_quotas_get_tenant_from_request_context(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
def test_quotas_get_project_from_request_context(self):
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
res = self.api.get(_get_path('quotas/tenant', fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
self.assertEqual(200, res.status_int)
|
||||
quota = self.deserialize(res)
|
||||
self.assertEqual(quota['tenant']['tenant_id'], tenant_id)
|
||||
# NOTE(ralonsoh): the Quota API keeps "tenant" and "project" methods
|
||||
# for compatibility. "tenant" is already deprecated and will be
|
||||
# removed.
|
||||
for key in ('project', 'tenant'):
|
||||
res = self.api.get(_get_path('quotas/' + key, fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
self.assertEqual(200, res.status_int)
|
||||
quota = self.deserialize(res)
|
||||
self.assertEqual(quota[key][key + '_id'], project_id)
|
||||
|
||||
def test_quotas_get_tenant_from_empty_request_context_returns_400(self):
|
||||
def test_quotas_get_project_from_empty_request_context_returns_400(self):
|
||||
env = {'neutron.context': context.Context('', '',
|
||||
is_admin=True)}
|
||||
res = self.api.get(_get_path('quotas/tenant', fmt=self.fmt),
|
||||
@ -398,20 +402,20 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase):
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_make_reservation_resource_unknown_raises(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
project_id = 'project_id1'
|
||||
self.assertRaises(exceptions.QuotaResourceUnknown,
|
||||
quota.QUOTAS.make_reservation,
|
||||
context.get_admin_context(),
|
||||
tenant_id,
|
||||
project_id,
|
||||
{'foobar': 1},
|
||||
plugin=None)
|
||||
|
||||
def test_make_reservation_negative_delta_raises(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
project_id = 'project_id1'
|
||||
self.assertRaises(exceptions.InvalidQuotaValue,
|
||||
quota.QUOTAS.make_reservation,
|
||||
context.get_admin_context(),
|
||||
tenant_id,
|
||||
project_id,
|
||||
{'network': -1},
|
||||
plugin=None)
|
||||
|
||||
@ -441,34 +445,34 @@ class QuotaExtensionCfgTestCase(QuotaExtensionTestCase):
|
||||
'extra1': qconf.DEFAULT_QUOTA})
|
||||
|
||||
def test_show_quotas_with_admin(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=True)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env)
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_show_quotas_without_admin_forbidden(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=False)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.get(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env, expect_errors=True)
|
||||
self.assertEqual(403, res.status_int)
|
||||
|
||||
def test_update_quotas_forbidden(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
project_id = 'project_id1'
|
||||
quotas = {'quota': {'network': 100}}
|
||||
res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.put(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
self.serialize(quotas),
|
||||
expect_errors=True)
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_delete_quotas_forbidden(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=False)}
|
||||
res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt),
|
||||
res = self.api.delete(_get_path('quotas', id=project_id, fmt=self.fmt),
|
||||
extra_environ=env, expect_errors=True)
|
||||
self.assertEqual(403, res.status_int)
|
||||
|
||||
@ -476,7 +480,7 @@ class QuotaExtensionCfgTestCase(QuotaExtensionTestCase):
|
||||
class TestDbQuotaDriver(base.BaseTestCase):
|
||||
"""Test for neutron.db.quota.driver.DbQuotaDriver."""
|
||||
|
||||
def test_get_tenant_quotas_arg(self):
|
||||
def test_get_project_quotas_arg(self):
|
||||
"""Call neutron.db.quota.driver.DbQuotaDriver._get_quotas."""
|
||||
|
||||
quota_driver = driver.DbQuotaDriver()
|
||||
@ -484,20 +488,20 @@ class TestDbQuotaDriver(base.BaseTestCase):
|
||||
|
||||
foo_quotas = {'network': 5}
|
||||
default_quotas = {'network': 10}
|
||||
target_tenant = 'foo'
|
||||
target_project = 'foo'
|
||||
|
||||
with mock.patch.object(driver.DbQuotaDriver,
|
||||
'get_tenant_quotas',
|
||||
return_value=foo_quotas) as get_tenant_quotas:
|
||||
'get_project_quotas',
|
||||
return_value=foo_quotas) as get_project_quotas:
|
||||
|
||||
quotas = quota_driver._get_quotas(ctx,
|
||||
target_tenant,
|
||||
target_project,
|
||||
default_quotas)
|
||||
|
||||
self.assertEqual(quotas, foo_quotas)
|
||||
get_tenant_quotas.assert_called_once_with(ctx,
|
||||
get_project_quotas.assert_called_once_with(ctx,
|
||||
default_quotas,
|
||||
target_tenant)
|
||||
target_project)
|
||||
|
||||
|
||||
class TestQuotaDriverLoad(base.BaseTestCase):
|
||||
|
@ -67,9 +67,9 @@ class DetailQuotaExtensionDbTestCase(DetailQuotaExtensionTestCase):
|
||||
fmt = 'json'
|
||||
|
||||
def test_show_detail_quotas(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id)}
|
||||
res = self.api.get(_get_path('quotas', id=project_id,
|
||||
fmt=self.fmt,
|
||||
endpoint=DEFAULT_QUOTAS_ACTION),
|
||||
extra_environ=env)
|
||||
@ -95,10 +95,10 @@ class DetailQuotaExtensionDbTestCase(DetailQuotaExtensionTestCase):
|
||||
'quota_network', -10, group='QUOTAS')
|
||||
cfg.CONF.set_override(
|
||||
'quota_subnet', -50, group='QUOTAS')
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id,
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id,
|
||||
is_admin=True)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
||||
res = self.api.get(_get_path('quotas', id=project_id,
|
||||
fmt=self.fmt,
|
||||
endpoint=DEFAULT_QUOTAS_ACTION),
|
||||
extra_environ=env)
|
||||
@ -118,10 +118,10 @@ class DetailQuotaExtensionDbTestCase(DetailQuotaExtensionTestCase):
|
||||
quota['quota']['port']['limit'])
|
||||
|
||||
def test_show_detail_quotas_with_admin(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=True)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
||||
res = self.api.get(_get_path('quotas', id=project_id,
|
||||
fmt=self.fmt,
|
||||
endpoint=DEFAULT_QUOTAS_ACTION),
|
||||
extra_environ=env)
|
||||
@ -141,10 +141,10 @@ class DetailQuotaExtensionDbTestCase(DetailQuotaExtensionTestCase):
|
||||
quota['quota']['port']['limit'])
|
||||
|
||||
def test_detail_quotas_without_admin_forbidden_returns_403(self):
|
||||
tenant_id = 'tenant_id1'
|
||||
env = {'neutron.context': context.Context('', tenant_id + '2',
|
||||
project_id = 'project_id1'
|
||||
env = {'neutron.context': context.Context('', project_id + '2',
|
||||
is_admin=False)}
|
||||
res = self.api.get(_get_path('quotas', id=tenant_id,
|
||||
res = self.api.get(_get_path('quotas', id=project_id,
|
||||
fmt=self.fmt,
|
||||
endpoint=DEFAULT_QUOTAS_ACTION),
|
||||
extra_environ=env, expect_errors=True)
|
||||
|
@ -45,7 +45,9 @@ class BaseTestTrackedResources(test_plugin.Ml2PluginV2TestCase,
|
||||
test_db_base_plugin_v2.NeutronDbPluginV2TestCase.quota_db_driver = (
|
||||
'neutron.db.quota.driver.DbQuotaDriver')
|
||||
super(BaseTestTrackedResources, self).setUp()
|
||||
self._tenant_id = uuidutils.generate_uuid()
|
||||
self._project_id = uuidutils.generate_uuid()
|
||||
# TODO(ralonsoh): "tenant_id" reference should be removed.
|
||||
self._tenant_id = self._project_id
|
||||
|
||||
@staticmethod
|
||||
def _cleanup():
|
||||
@ -54,7 +56,7 @@ class BaseTestTrackedResources(test_plugin.Ml2PluginV2TestCase,
|
||||
|
||||
def _test_init(self, resource_name):
|
||||
quota_db_api.set_quota_usage(
|
||||
self.ctx, resource_name, self._tenant_id)
|
||||
self.ctx, resource_name, self._project_id)
|
||||
|
||||
|
||||
class BaseTestEventHandler(object):
|
||||
@ -88,7 +90,7 @@ class BaseTestEventHandler(object):
|
||||
if item:
|
||||
model = self.handler_mock.call_args_list[call_idx][0][-1]
|
||||
self.assertEqual(model['id'], item['id'])
|
||||
self.assertEqual(model['tenant_id'], item['tenant_id'])
|
||||
self.assertEqual(model['project_id'], item['project_id'])
|
||||
call_idx = call_idx - 1
|
||||
|
||||
|
||||
@ -140,7 +142,7 @@ class TestTrackedResourcesEventHandler(BaseTestEventHandler,
|
||||
self._test_init('subnetpool')
|
||||
pool = self._make_subnetpool('json', ['10.0.0.0/8'],
|
||||
name='meh',
|
||||
tenant_id=self._tenant_id)['subnetpool']
|
||||
tenant_id=self._project_id)['subnetpool']
|
||||
self._verify_event_handler_calls(pool)
|
||||
self._delete('subnetpools', pool['id'])
|
||||
self._verify_event_handler_calls(pool, expected_call_count=2)
|
||||
@ -148,7 +150,8 @@ class TestTrackedResourcesEventHandler(BaseTestEventHandler,
|
||||
def test_create_delete_securitygroup_triggers_event(self):
|
||||
self._test_init('security_group')
|
||||
sec_group = self._make_security_group(
|
||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
||||
'json', 'meh', 'meh',
|
||||
project_id=self._project_id)['security_group']
|
||||
# When a security group is created it also creates 2 rules, therefore
|
||||
# there will be three calls and we need to verify the first
|
||||
self._verify_event_handler_calls([None, None, sec_group],
|
||||
@ -161,9 +164,10 @@ class TestTrackedResourcesEventHandler(BaseTestEventHandler,
|
||||
def test_create_delete_securitygrouprule_triggers_event(self):
|
||||
self._test_init('security_group_rule')
|
||||
sec_group = self._make_security_group(
|
||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
||||
'json', 'meh', 'meh',
|
||||
project_id=self._project_id)['security_group']
|
||||
rule_req = self._build_security_group_rule(
|
||||
sec_group['id'], 'ingress', 'TCP', tenant_id=self._tenant_id)
|
||||
sec_group['id'], 'ingress', 'TCP', tenant_id=self._project_id)
|
||||
sec_group_rule = self._make_security_group_rule(
|
||||
'json', rule_req)['security_group_rule']
|
||||
# When a security group is created it also creates 2 rules, therefore
|
||||
@ -213,8 +217,8 @@ class TestL3ResourcesEventHandler(BaseTestEventHandler,
|
||||
class TestTrackedResources(BaseTestTrackedResources):
|
||||
|
||||
def _verify_dirty_bit(self, resource_name, expected_value=True):
|
||||
usage = quota_db_api.get_quota_usage_by_resource_and_tenant(
|
||||
self.ctx, resource_name, self._tenant_id)
|
||||
usage = quota_db_api.get_quota_usage_by_resource_and_project(
|
||||
self.ctx, resource_name, self._project_id)
|
||||
self.assertEqual(expected_value, usage.dirty)
|
||||
|
||||
def test_create_delete_network_marks_dirty(self):
|
||||
@ -223,14 +227,14 @@ class TestTrackedResources(BaseTestTrackedResources):
|
||||
self._verify_dirty_bit('network')
|
||||
# Clear the dirty bit
|
||||
quota_db_api.set_quota_usage_dirty(
|
||||
self.ctx, 'network', self._tenant_id, dirty=False)
|
||||
self.ctx, 'network', self._project_id, dirty=False)
|
||||
self._delete('networks', net['id'])
|
||||
self._verify_dirty_bit('network')
|
||||
|
||||
def test_list_networks_clears_dirty(self):
|
||||
self._test_init('network')
|
||||
net = self._make_network('json', 'meh', True)['network']
|
||||
self.ctx.tenant_id = net['tenant_id']
|
||||
self.ctx.project_id = net['project_id']
|
||||
self._list('networks', neutron_context=self.ctx)
|
||||
self._verify_dirty_bit('network', expected_value=False)
|
||||
|
||||
@ -241,7 +245,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
||||
self._verify_dirty_bit('port')
|
||||
# Clear the dirty bit
|
||||
quota_db_api.set_quota_usage_dirty(
|
||||
self.ctx, 'port', self._tenant_id, dirty=False)
|
||||
self.ctx, 'port', self._project_id, dirty=False)
|
||||
self._delete('ports', port['id'])
|
||||
self._verify_dirty_bit('port')
|
||||
|
||||
@ -249,7 +253,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
||||
self._test_init('port')
|
||||
net = self._make_network('json', 'meh', True)['network']
|
||||
port = self._make_port('json', net['id'])['port']
|
||||
self.ctx.tenant_id = port['tenant_id']
|
||||
self.ctx.project_id = port['project_id']
|
||||
self._list('ports', neutron_context=self.ctx)
|
||||
self._verify_dirty_bit('port', expected_value=False)
|
||||
|
||||
@ -261,7 +265,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
||||
self._verify_dirty_bit('subnet')
|
||||
# Clear the dirty bit
|
||||
quota_db_api.set_quota_usage_dirty(
|
||||
self.ctx, 'subnet', self._tenant_id, dirty=False)
|
||||
self.ctx, 'subnet', self._project_id, dirty=False)
|
||||
self._delete('subnets', subnet['id'])
|
||||
self._verify_dirty_bit('subnet')
|
||||
|
||||
@ -274,7 +278,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
||||
self._verify_dirty_bit('subnet')
|
||||
# Clear the dirty bit
|
||||
quota_db_api.set_quota_usage_dirty(
|
||||
self.ctx, 'subnet', self._tenant_id, dirty=False)
|
||||
self.ctx, 'subnet', self._project_id, dirty=False)
|
||||
self._delete('networks', net['network']['id'])
|
||||
self._verify_dirty_bit('network')
|
||||
self._verify_dirty_bit('subnet')
|
||||
@ -284,7 +288,7 @@ class TestTrackedResources(BaseTestTrackedResources):
|
||||
net = self._make_network('json', 'meh', True)
|
||||
subnet = self._make_subnet('json', net, '10.0.0.1',
|
||||
'10.0.0.0/24')['subnet']
|
||||
self.ctx.tenant_id = subnet['tenant_id']
|
||||
self.ctx.project_id = subnet['project_id']
|
||||
self._list('subnets', neutron_context=self.ctx)
|
||||
self._verify_dirty_bit('subnet', expected_value=False)
|
||||
|
||||
@ -292,11 +296,11 @@ class TestTrackedResources(BaseTestTrackedResources):
|
||||
self._test_init('subnetpool')
|
||||
pool = self._make_subnetpool('json', ['10.0.0.0/8'],
|
||||
name='meh',
|
||||
tenant_id=self._tenant_id)['subnetpool']
|
||||
tenant_id=self._project_id)['subnetpool']
|
||||
self._verify_dirty_bit('subnetpool')
|
||||
# Clear the dirty bit
|
||||
quota_db_api.set_quota_usage_dirty(
|
||||
self.ctx, 'subnetpool', self._tenant_id, dirty=False)
|
||||
self.ctx, 'subnetpool', self._project_id, dirty=False)
|
||||
self._delete('subnetpools', pool['id'])
|
||||
self._verify_dirty_bit('subnetpool')
|
||||
|
||||
@ -304,51 +308,51 @@ class TestTrackedResources(BaseTestTrackedResources):
|
||||
self._test_init('subnetpool')
|
||||
pool = self._make_subnetpool('json', ['10.0.0.0/8'],
|
||||
name='meh',
|
||||
tenant_id=self._tenant_id)['subnetpool']
|
||||
self.ctx.tenant_id = pool['tenant_id']
|
||||
tenant_id=self._project_id)['subnetpool']
|
||||
self.ctx.project_id = pool['project_id']
|
||||
self._list('subnetpools', neutron_context=self.ctx)
|
||||
self._verify_dirty_bit('subnetpool', expected_value=False)
|
||||
|
||||
def test_create_delete_securitygroup_marks_dirty(self):
|
||||
self._test_init('security_group')
|
||||
sec_group = self._make_security_group(
|
||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
||||
'json', 'meh', 'meh', tenant_id=self._project_id)['security_group']
|
||||
self._verify_dirty_bit('security_group')
|
||||
# Clear the dirty bit
|
||||
quota_db_api.set_quota_usage_dirty(
|
||||
self.ctx, 'security_group', self._tenant_id, dirty=False)
|
||||
self.ctx, 'security_group', self._project_id, dirty=False)
|
||||
self._delete('security-groups', sec_group['id'])
|
||||
self._verify_dirty_bit('security_group')
|
||||
|
||||
def test_list_securitygroups_clears_dirty(self):
|
||||
self._test_init('security_group')
|
||||
self._make_security_group(
|
||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
||||
self.ctx.tenant_id = self._tenant_id
|
||||
'json', 'meh', 'meh', tenant_id=self._project_id)['security_group']
|
||||
self.ctx.project_id = self._project_id
|
||||
self._list('security-groups', neutron_context=self.ctx)
|
||||
self._verify_dirty_bit('security_group', expected_value=False)
|
||||
|
||||
def test_create_delete_securitygrouprule_marks_dirty(self):
|
||||
self._test_init('security_group_rule')
|
||||
sec_group = self._make_security_group(
|
||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
||||
'json', 'meh', 'meh', tenant_id=self._project_id)['security_group']
|
||||
rule_req = self._build_security_group_rule(
|
||||
sec_group['id'], 'ingress', 'TCP', tenant_id=self._tenant_id)
|
||||
sec_group['id'], 'ingress', 'TCP', tenant_id=self._project_id)
|
||||
sec_group_rule = self._make_security_group_rule(
|
||||
'json', rule_req)['security_group_rule']
|
||||
self._verify_dirty_bit('security_group_rule')
|
||||
# Clear the dirty bit
|
||||
quota_db_api.set_quota_usage_dirty(
|
||||
self.ctx, 'security_group_rule', self._tenant_id, dirty=False)
|
||||
self.ctx, 'security_group_rule', self._project_id, dirty=False)
|
||||
self._delete('security-group-rules', sec_group_rule['id'])
|
||||
self._verify_dirty_bit('security_group_rule')
|
||||
|
||||
def test_list_securitygrouprules_clears_dirty(self):
|
||||
self._test_init('security_group_rule')
|
||||
self._make_security_group(
|
||||
'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group']
|
||||
'json', 'meh', 'meh', tenant_id=self._project_id)['security_group']
|
||||
# As the security group create operation also creates 2 security group
|
||||
# rules there is no need to explicitly create any rule
|
||||
self.ctx.tenant_id = self._tenant_id
|
||||
self.ctx.project_id = self._project_id
|
||||
self._list('security-group-rules', neutron_context=self.ctx)
|
||||
self._verify_dirty_bit('security_group_rule', expected_value=False)
|
||||
|
@ -75,22 +75,22 @@ class TestResource(base.DietTestCase):
|
||||
|
||||
class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
|
||||
def _add_data(self, tenant_id=None):
|
||||
def _add_data(self, project_id=None):
|
||||
session = db_api.get_writer_session()
|
||||
with session.begin():
|
||||
tenant_id = tenant_id or self.tenant_id
|
||||
project_id = project_id or self.project_id
|
||||
session.add(test_quota.MehModel(
|
||||
meh='meh_%s' % uuidutils.generate_uuid(),
|
||||
tenant_id=tenant_id))
|
||||
project_id=project_id))
|
||||
session.add(test_quota.MehModel(
|
||||
meh='meh_%s' % uuidutils.generate_uuid(),
|
||||
tenant_id=tenant_id))
|
||||
project_id=project_id))
|
||||
|
||||
def _delete_data(self):
|
||||
session = db_api.get_writer_session()
|
||||
with session.begin():
|
||||
query = session.query(test_quota.MehModel).filter_by(
|
||||
tenant_id=self.tenant_id)
|
||||
project_id=self.project_id)
|
||||
for item in query:
|
||||
session.delete(item)
|
||||
|
||||
@ -98,7 +98,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
session = db_api.get_writer_session()
|
||||
with session.begin():
|
||||
query = session.query(test_quota.MehModel).filter_by(
|
||||
tenant_id=self.tenant_id)
|
||||
project_id=self.project_id)
|
||||
for item in query:
|
||||
item['meh'] = 'meh-%s' % item['meh']
|
||||
session.add(item)
|
||||
@ -109,9 +109,9 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
self.setup_coreplugin(DB_PLUGIN_KLASS)
|
||||
self.resource = 'meh'
|
||||
self.other_resource = 'othermeh'
|
||||
self.tenant_id = 'meh'
|
||||
self.project_id = 'meh'
|
||||
self.context = context.Context(
|
||||
user_id='', tenant_id=self.tenant_id, is_admin=False)
|
||||
user_id='', project_id=self.project_id, is_admin=False)
|
||||
|
||||
def _create_resource(self):
|
||||
res = resource.TrackedResource(
|
||||
@ -133,7 +133,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
|
||||
def test_count_first_call_with_dirty_false(self):
|
||||
quota_api.set_quota_usage(
|
||||
self.context, self.resource, self.tenant_id, in_use=1)
|
||||
self.context, self.resource, self.project_id, in_use=1)
|
||||
res = self._create_resource()
|
||||
self._add_data()
|
||||
# explicitly set dirty flag to False
|
||||
@ -141,17 +141,17 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
self.context, self.resource, dirty=False)
|
||||
# Expect correct count to be returned anyway since the first call to
|
||||
# count() always resyncs with the db
|
||||
self.assertEqual(2, res.count(self.context, None, self.tenant_id))
|
||||
self.assertEqual(2, res.count(self.context, None, self.project_id))
|
||||
|
||||
def test_count_reserved(self):
|
||||
res = self._create_resource()
|
||||
quota_api.create_reservation(self.context, self.tenant_id,
|
||||
quota_api.create_reservation(self.context, self.project_id,
|
||||
{res.name: 1})
|
||||
self.assertEqual(1, res.count_reserved(self.context, self.tenant_id))
|
||||
self.assertEqual(1, res.count_reserved(self.context, self.project_id))
|
||||
|
||||
def test_count_used_first_call_with_dirty_false(self):
|
||||
quota_api.set_quota_usage(
|
||||
self.context, self.resource, self.tenant_id, in_use=1)
|
||||
self.context, self.resource, self.project_id, in_use=1)
|
||||
res = self._create_resource()
|
||||
self._add_data()
|
||||
# explicitly set dirty flag to False
|
||||
@ -160,18 +160,18 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
# Expect correct count_used to be returned
|
||||
# anyway since the first call to
|
||||
# count_used() always resyncs with the db
|
||||
self.assertEqual(2, res.count_used(self.context, self.tenant_id))
|
||||
self.assertEqual(2, res.count_used(self.context, self.project_id))
|
||||
|
||||
def _test_count(self):
|
||||
res = self._create_resource()
|
||||
quota_api.set_quota_usage(
|
||||
self.context, res.name, self.tenant_id, in_use=0)
|
||||
self.context, res.name, self.project_id, in_use=0)
|
||||
self._add_data()
|
||||
return res
|
||||
|
||||
def test_count_with_dirty_false(self):
|
||||
res = self._test_count()
|
||||
res.count(self.context, None, self.tenant_id)
|
||||
res.count(self.context, None, self.project_id)
|
||||
# At this stage count has been invoked, and the dirty flag should be
|
||||
# false. Another invocation of count should not query the model class
|
||||
set_quota = 'neutron.db.quota.api.set_quota_usage'
|
||||
@ -179,11 +179,11 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
self.assertEqual(0, mock_set_quota.call_count)
|
||||
self.assertEqual(2, res.count(self.context,
|
||||
None,
|
||||
self.tenant_id))
|
||||
self.project_id))
|
||||
|
||||
def test_count_used_with_dirty_false(self):
|
||||
res = self._test_count()
|
||||
res.count_used(self.context, self.tenant_id)
|
||||
res.count_used(self.context, self.project_id)
|
||||
# At this stage count_used has been invoked,
|
||||
# and the dirty flag should be false. Another invocation
|
||||
# of count_used should not query the model class
|
||||
@ -191,7 +191,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
with mock.patch(set_quota) as mock_set_quota:
|
||||
self.assertEqual(0, mock_set_quota.call_count)
|
||||
self.assertEqual(2, res.count_used(self.context,
|
||||
self.tenant_id))
|
||||
self.project_id))
|
||||
|
||||
def test_count_with_dirty_true_resync(self):
|
||||
res = self._test_count()
|
||||
@ -199,7 +199,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
# set_quota_usage has been invoked with the correct parameters
|
||||
self.assertEqual(2, res.count(self.context,
|
||||
None,
|
||||
self.tenant_id,
|
||||
self.project_id,
|
||||
resync_usage=True))
|
||||
|
||||
def test_count_used_with_dirty_true_resync(self):
|
||||
@ -207,7 +207,7 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
# Expect correct count_used to be returned, which also implies
|
||||
# set_quota_usage has been invoked with the correct parameters
|
||||
self.assertEqual(2, res.count_used(self.context,
|
||||
self.tenant_id,
|
||||
self.project_id,
|
||||
resync_usage=True))
|
||||
|
||||
def test_count_with_dirty_true_resync_calls_set_quota_usage(self):
|
||||
@ -216,11 +216,11 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||
quota_api.set_quota_usage_dirty(self.context,
|
||||
self.resource,
|
||||
self.tenant_id)
|
||||
res.count(self.context, None, self.tenant_id,
|
||||
self.project_id)
|
||||
res.count(self.context, None, self.project_id,
|
||||
resync_usage=True)
|
||||
mock_set_quota_usage.assert_called_once_with(
|
||||
self.context, self.resource, self.tenant_id, in_use=2)
|
||||
self.context, self.resource, self.project_id, in_use=2)
|
||||
|
||||
def test_count_used_with_dirty_true_resync_calls_set_quota_usage(self):
|
||||
res = self._test_count()
|
||||
@ -228,25 +228,25 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||
quota_api.set_quota_usage_dirty(self.context,
|
||||
self.resource,
|
||||
self.tenant_id)
|
||||
res.count_used(self.context, self.tenant_id,
|
||||
self.project_id)
|
||||
res.count_used(self.context, self.project_id,
|
||||
resync_usage=True)
|
||||
mock_set_quota_usage.assert_called_once_with(
|
||||
self.context, self.resource, self.tenant_id, in_use=2)
|
||||
self.context, self.resource, self.project_id, in_use=2)
|
||||
|
||||
def test_count_with_dirty_true_no_usage_info(self):
|
||||
res = self._create_resource()
|
||||
self._add_data()
|
||||
# Invoke count without having usage info in DB - Expect correct
|
||||
# count to be returned
|
||||
self.assertEqual(2, res.count(self.context, None, self.tenant_id))
|
||||
self.assertEqual(2, res.count(self.context, None, self.project_id))
|
||||
|
||||
def test_count_used_with_dirty_true_no_usage_info(self):
|
||||
res = self._create_resource()
|
||||
self._add_data()
|
||||
# Invoke count_used without having usage info in DB - Expect correct
|
||||
# count_used to be returned
|
||||
self.assertEqual(2, res.count_used(self.context, self.tenant_id))
|
||||
self.assertEqual(2, res.count_used(self.context, self.project_id))
|
||||
|
||||
def test_count_with_dirty_true_no_usage_info_calls_set_quota_usage(self):
|
||||
res = self._create_resource()
|
||||
@ -255,10 +255,10 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||
quota_api.set_quota_usage_dirty(self.context,
|
||||
self.resource,
|
||||
self.tenant_id)
|
||||
res.count(self.context, None, self.tenant_id, resync_usage=True)
|
||||
self.project_id)
|
||||
res.count(self.context, None, self.project_id, resync_usage=True)
|
||||
mock_set_quota_usage.assert_called_once_with(
|
||||
self.context, self.resource, self.tenant_id, in_use=2)
|
||||
self.context, self.resource, self.project_id, in_use=2)
|
||||
|
||||
def test_count_used_with_dirty_true_no_usage_info_calls_set_quota_usage(
|
||||
self):
|
||||
@ -268,44 +268,44 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||
quota_api.set_quota_usage_dirty(self.context,
|
||||
self.resource,
|
||||
self.tenant_id)
|
||||
res.count_used(self.context, self.tenant_id, resync_usage=True)
|
||||
self.project_id)
|
||||
res.count_used(self.context, self.project_id, resync_usage=True)
|
||||
mock_set_quota_usage.assert_called_once_with(
|
||||
self.context, self.resource, self.tenant_id, in_use=2)
|
||||
self.context, self.resource, self.project_id, in_use=2)
|
||||
|
||||
def test_add_delete_data_triggers_event(self):
|
||||
res = self._create_resource()
|
||||
other_res = self._create_other_resource()
|
||||
# Validate dirty tenants since mock does not work well with SQLAlchemy
|
||||
# Validate dirty projects since mock does not work well with SQLAlchemy
|
||||
# event handlers.
|
||||
self._add_data()
|
||||
self._add_data('someone_else')
|
||||
self.assertEqual(2, len(res._dirty_tenants))
|
||||
self.assertEqual(2, len(res._dirty_projects))
|
||||
# Also, the dirty flag should not be set for other resources
|
||||
self.assertEqual(0, len(other_res._dirty_tenants))
|
||||
self.assertIn(self.tenant_id, res._dirty_tenants)
|
||||
self.assertIn('someone_else', res._dirty_tenants)
|
||||
self.assertEqual(0, len(other_res._dirty_projects))
|
||||
self.assertIn(self.project_id, res._dirty_projects)
|
||||
self.assertIn('someone_else', res._dirty_projects)
|
||||
|
||||
def test_delete_data_triggers_event(self):
|
||||
res = self._create_resource()
|
||||
self._add_data()
|
||||
self._add_data('someone_else')
|
||||
# Artificially clear _dirty_tenants
|
||||
res._dirty_tenants.clear()
|
||||
# Artificially clear _dirty_projects
|
||||
res._dirty_projects.clear()
|
||||
self._delete_data()
|
||||
# We did not delete "someone_else", so expect only a single dirty
|
||||
# tenant
|
||||
self.assertEqual(1, len(res._dirty_tenants))
|
||||
self.assertIn(self.tenant_id, res._dirty_tenants)
|
||||
# project
|
||||
self.assertEqual(1, len(res._dirty_projects))
|
||||
self.assertIn(self.project_id, res._dirty_projects)
|
||||
|
||||
def test_update_does_not_trigger_event(self):
|
||||
res = self._create_resource()
|
||||
self._add_data()
|
||||
self._add_data('someone_else')
|
||||
# Artificially clear _dirty_tenants
|
||||
res._dirty_tenants.clear()
|
||||
# Artificially clear _dirty_projects
|
||||
res._dirty_projects.clear()
|
||||
self._update_data()
|
||||
self.assertEqual(0, len(res._dirty_tenants))
|
||||
self.assertEqual(0, len(res._dirty_projects))
|
||||
|
||||
def test_mark_dirty(self):
|
||||
res = self._create_resource()
|
||||
@ -316,11 +316,11 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
res.mark_dirty(self.context)
|
||||
self.assertEqual(2, mock_set_quota_usage.call_count)
|
||||
mock_set_quota_usage.assert_any_call(
|
||||
self.context, self.resource, self.tenant_id)
|
||||
self.context, self.resource, self.project_id)
|
||||
mock_set_quota_usage.assert_any_call(
|
||||
self.context, self.resource, 'someone_else')
|
||||
|
||||
def test_mark_dirty_no_dirty_tenant(self):
|
||||
def test_mark_dirty_no_dirty_project(self):
|
||||
res = self._create_resource()
|
||||
set_quota_usage = 'neutron.db.quota.api.set_quota_usage_dirty'
|
||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||
@ -331,14 +331,14 @@ class TestTrackedResource(testlib_api.SqlTestCase):
|
||||
res = self._create_resource()
|
||||
self._add_data()
|
||||
res.mark_dirty(self.context)
|
||||
# self.tenant_id now is out of sync
|
||||
# self.project_id now is out of sync
|
||||
set_quota_usage = 'neutron.db.quota.api.set_quota_usage'
|
||||
with mock.patch(set_quota_usage) as mock_set_quota_usage:
|
||||
res.resync(self.context, self.tenant_id)
|
||||
res.resync(self.context, self.project_id)
|
||||
# and now it should be in sync
|
||||
self.assertNotIn(self.tenant_id, res._out_of_sync_tenants)
|
||||
self.assertNotIn(self.project_id, res._out_of_sync_projects)
|
||||
mock_set_quota_usage.assert_called_once_with(
|
||||
self.context, self.resource, self.tenant_id, in_use=2)
|
||||
self.context, self.resource, self.project_id, in_use=2)
|
||||
|
||||
|
||||
class Test_CountResource(base.BaseTestCase):
|
||||
@ -355,15 +355,15 @@ class Test_CountResource(base.BaseTestCase):
|
||||
|
||||
context = mock.Mock()
|
||||
collection_name = 'floatingips'
|
||||
tenant_id = 'fakeid'
|
||||
project_id = 'fakeid'
|
||||
self.assertRaises(
|
||||
NotImplementedError,
|
||||
resource._count_resource, context, collection_name, tenant_id)
|
||||
resource._count_resource, context, collection_name, project_id)
|
||||
|
||||
for plugin in plugins.values():
|
||||
for func in (plugin.get_floatingips_count, plugin.get_floatingips):
|
||||
func.assert_called_with(
|
||||
context, filters={'tenant_id': [tenant_id]})
|
||||
context, filters={'project_id': [project_id]})
|
||||
|
||||
def test_core_plugin_checked_first(self):
|
||||
plugin1 = mock.Mock()
|
||||
@ -378,6 +378,6 @@ class Test_CountResource(base.BaseTestCase):
|
||||
|
||||
context = mock.Mock()
|
||||
collection_name = 'floatingips'
|
||||
tenant_id = 'fakeid'
|
||||
project_id = 'fakeid'
|
||||
self.assertEqual(
|
||||
10, resource._count_resource(context, collection_name, tenant_id))
|
||||
10, resource._count_resource(context, collection_name, project_id))
|
||||
|
@ -101,7 +101,7 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
||||
'TrackedResource.resync') as mock_resync:
|
||||
self.registry.set_tracked_resource('meh', test_quota.MehModel)
|
||||
self.registry.register_resource_by_name('meh')
|
||||
resource_registry.resync_resource(mock.ANY, 'meh', 'tenant_id')
|
||||
resource_registry.resync_resource(mock.ANY, 'meh', 'project_id')
|
||||
self.assertEqual(0, mock_resync.call_count)
|
||||
|
||||
def test_resync_tracked_resource(self):
|
||||
@ -109,14 +109,14 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
||||
'TrackedResource.resync') as mock_resync:
|
||||
self.registry.set_tracked_resource('meh', test_quota.MehModel)
|
||||
self.registry.register_resource_by_name('meh')
|
||||
resource_registry.resync_resource(mock.ANY, 'meh', 'tenant_id')
|
||||
mock_resync.assert_called_once_with(mock.ANY, 'tenant_id')
|
||||
resource_registry.resync_resource(mock.ANY, 'meh', 'project_id')
|
||||
mock_resync.assert_called_once_with(mock.ANY, 'project_id')
|
||||
|
||||
def test_resync_non_tracked_resource(self):
|
||||
with mock.patch('neutron.quota.resource.'
|
||||
'TrackedResource.resync') as mock_resync:
|
||||
self.registry.register_resource_by_name('meh')
|
||||
resource_registry.resync_resource(mock.ANY, 'meh', 'tenant_id')
|
||||
resource_registry.resync_resource(mock.ANY, 'meh', 'project_id')
|
||||
self.assertEqual(0, mock_resync.call_count)
|
||||
|
||||
def test_set_resources_dirty_invoked_with_tracking_disabled(self):
|
||||
@ -132,7 +132,7 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
||||
self.assertEqual(0, mock_mark_dirty.call_count)
|
||||
|
||||
def test_set_resources_dirty_no_dirty_resource(self):
|
||||
ctx = context.Context('user_id', 'tenant_id',
|
||||
ctx = context.Context('user_id', 'project_id',
|
||||
is_admin=False, is_advsvc=False)
|
||||
with mock.patch('neutron.quota.resource.'
|
||||
'TrackedResource.mark_dirty') as mock_mark_dirty:
|
||||
@ -140,12 +140,12 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
||||
self.registry.register_resource_by_name('meh')
|
||||
res = self.registry.get_resource('meh')
|
||||
# This ensures dirty is false
|
||||
res._dirty_tenants.clear()
|
||||
res._dirty_projects.clear()
|
||||
resource_registry.set_resources_dirty(ctx)
|
||||
self.assertEqual(0, mock_mark_dirty.call_count)
|
||||
|
||||
def test_set_resources_dirty_no_tracked_resource(self):
|
||||
ctx = context.Context('user_id', 'tenant_id',
|
||||
ctx = context.Context('user_id', 'project_id',
|
||||
is_admin=False, is_advsvc=False)
|
||||
with mock.patch('neutron.quota.resource.'
|
||||
'TrackedResource.mark_dirty') as mock_mark_dirty:
|
||||
@ -154,7 +154,7 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
||||
self.assertEqual(0, mock_mark_dirty.call_count)
|
||||
|
||||
def test_set_resources_dirty(self):
|
||||
ctx = context.Context('user_id', 'tenant_id',
|
||||
ctx = context.Context('user_id', 'project_id',
|
||||
is_admin=False, is_advsvc=False)
|
||||
with mock.patch('neutron.quota.resource.'
|
||||
'TrackedResource.mark_dirty') as mock_mark_dirty:
|
||||
@ -163,6 +163,6 @@ class TestAuxiliaryFunctions(base.DietTestCase):
|
||||
self.registry.resources['meh']._track_resource_events = True
|
||||
res = self.registry.get_resource('meh')
|
||||
# This ensures dirty is true
|
||||
res._dirty_tenants.add('tenant_id')
|
||||
res._dirty_projects.add('project_id')
|
||||
resource_registry.set_resources_dirty(ctx)
|
||||
mock_mark_dirty.assert_called_once_with(ctx)
|
||||
|
Loading…
Reference in New Issue
Block a user